Для примера рассмотрим работу с торгами(Trade) которую осуществляют какие-то пользователи(User), которые
могут заводить торги, выдвигать на них предложения(Offer) и подписывать контракты(Contract).
Сначала давайте инициализируем datascooter - подготовим его к работе: загрузите org.datascooter-..plugin-pack.zip
и распакуйте в любую директорию. Затем запустите eclipse и в Package Explorer кликните правой кнопкой на пустом месте -
в контектном меню выберите Import ... Existing Projects Into workspace, укажите директорию в которую распаковали архив
и проставьте чекбокс ... Copy projects into workspace. Откройте плагин org.datascooter.test и в пакете org.datascooter.test
выберите класс TradeTest. Замените путь к файлу настроек на свой - "[you file path]/datascooter.properties".
Кликните по классу TradeTest правой кнопкой - Run As ... JUnit Plugin Test ... И тестовый пример за десятую долю секунды
совершит пару сотен сложных операций сохранения и извлечения данных из реляционной базы данных. Если вы не меняли текст
файла datasource.properties в корневой директории плагина-
name=h2
contextId=h2
url=jdbc:h2:file:C:/my/test/data;LOCK_TIMEOUT=20000
user=sa
password=
current=true
То это будет маленькая и быстрая база H2 - она создаст директорию C:/my/test/ и базу данных data внутри и проведет
все операции с ней.
А теперь заглянем под капот:
1.Сначала инициализируем datascooter:
Если он используется в окружении еклипса - при старте надо использовать Eclipse.. провайдеры
DataScooter.start(
true,
"[you file path]/datascooter.properties",
new EclipseBuilderProvider(),
new EclipseConnectorProvider(),
new EclipseDataSourceProvider(),
new EclipseMappingProvider());
Если в простом Java проекте - то XML... провайдеры
DataScooter.start(
true,
"[you file path]/datascooter.properties",
new XMLBuilderProvider("[you file path].xml"),
new XMLConnectorProvider("[you file path].xml"),
new XMLDataSourceProvider("[you file path].xml"),
new XMLMappingProvider("[you file path].xml"));
Параметр true - указывает что после старта необходимо произвести валидацию таблиц - то есть проверить их наличие
и соответствие полей замаппированным полям объектов. Привести в соответствие - добавить нужную таблицу или поле.
Конечно - datascooter не планировался как замена администратора БД - поэтому он ипользует только минимально необходимый
для работы набор SQL DDL выражений и тонкая настройка индексов и особенностей хранения остается за вами.
TestUtils.dropAll(DataScooter.getDefault());
Удаляем все ипользуемые в тесте таблицы - для чистоты эксперимента. Внутри этот метод выглядит так:
public static void clearAll(IDataManager manager) throws EntityNotMappedException,
SQLException, BuildClauseException, IOException {
manager.clear(Trade.class.getName());
manager.clear(Contract.class.getName());
manager.clear(Offer.class.getName());
manager.clear(User.class.getName());
}
То есть мы командуем DataManager удалить(drop) хранилища таких-то замаппированных сущностей
DataScooter.getDefault().verifyTables(true);
Еще раз верифицируем таблицы - они будут созданы заново.
public void createUsers() {
user1 = new User("aa", "aaa");
user2 = new User("aa", "sss");
user3 = new User("aa", "aaa");
user4 = new User("aa", "aaa");
DataScooter.getDefault().save(user1);
DataScooter.getDefault().save(user2);
DataScooter.getDefault().save(user3);
DataScooter.getDefault().save(user4);
}
Создаем пользователей и сохраняем их в базу
public void createTrades() {
trade1 = new Trade("trade1", "cart", 10L, new Date(), new Date(), user1.getId());
trade2 = new Trade("trade2", "car", 10L, new Date(), new Date(), user4.getId());
trade3 = new Trade("trade3", "track", 10L, new Date(), new Date(), user2.getId());
trade4 = new Trade("trade4", "car", 10L, new Date(), new Date(), user3.getId());
DataScooter.getDefault().save(trade1);
DataScooter.getDefault().save(trade2);
DataScooter.getDefault().save(trade3);
DataScooter.getDefault().save(trade4);
DataScooter.getDefault().save(new ChildTrade(trade1, "177"));
DataScooter.getDefault().save(new ChildTrade(trade2, "2"));
DataScooter.getDefault().save(new ChildTrade(trade2, "2"));
DataScooter.getDefault().save(new ChildTrade(trade4, "0"));
DataScooter.getDefault().save(new ChildTrade(trade4, "44"));
}
Создаем торги и снова сохраняем - в последних пяти строчках создаются и сохраняются специальные типы торгов
- для иллюстрации маппинга наследования - об этом мы поговорм позже.
public void createOffers() {
offer1 = new Offer("off1", trade1.getId(), 55L, new Date(), new Date(), user4.getId());
offer2 = new Offer("off2", trade1.getId(), 55L, new Date(), new Date(), user2.getId());
offer3 = new Offer("off3", trade2.getId(), 55L, new Date(), new Date(), user2.getId());
offer4 = new Offer("off4", trade4.getId(), 55L, new Date(), new Date(), user2.getId());
DataScooter.getDefault().save(offer1);
DataScooter.getDefault().save(offer2);
DataScooter.getDefault().save(offer3);
DataScooter.getDefault().save(offer4);
}
Создаем и сохраняем предложения по торгам - код класса Offer выглядит так:
package org.datascooter.test.example;
import java.util.Date;
import org.datascooter.impl.Data;
public class Offer extends Data {
private static final long serialVersionUID = -7688647132004333854L;
public String name;
public String tradeId;
public Long price;
public Date start;
public Date stop;
public String userId;
public Offer() {
}
public Offer(String name, String tradeId, Long price, Date start, Date stop, String userId) {
this.name = name;
this.tradeId = tradeId;
this.price = price;
this.start = start;
this.stop = stop;
this.userId = userId;
}
@Override
public String toString() {
return name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTradeId() {
return tradeId;
}
public void setTradeId(String tradeId) {
this.tradeId = tradeId;
}
public Long getPrice() {
return price;
}
public void setPrice(Long price) {
this.price = price;
}
public Date getStart() {
return start;
}
public void setStart(Date start) {
this.start = start;
}
public Date getStop() {
return stop;
}
public void setStop(Date stop) {
this.stop = stop;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
}
А вот так выглядит маппинг этого класса:
Вам не нужно возиться с тегами XML - просто заполняете форму.Здесь вы можете видеть что маппируются как
простые атттрибуты так и ссылки на другие классы
public void createContracts() {
contract1 = new Contract("text1", trade1.getId(), offer1.getId());
contract2 = new Contract("text2", trade3.getId(), offer2.getId());
contract3 = new Contract("text3", trade2.getId(), offer3.getId());
contract4 = new Contract("text4", trade4.getId(), offer4.getId());
DataScooter.getDefault().save(contract1);
DataScooter.getDefault().save(contract2);
DataScooter.getDefault().save(contract3);
DataScooter.getDefault().save(contract4);
}
Создаем и сохраняем контракты между владельцем торга и владельцем выигравшего предложения
public void testReferenceInvoke() {
assertTrue(trade1.getUser().equals(user1));
List list = trade1.getOfferList();
List contractList = trade1.getContractList();
}
Здесь мы проверяем ReferenceInvoker - стандартный способ для извлечения связанных объектов -
его вызов может находиться
внутри метода объекта или в аспекте(AspectJ) перехватывающем этот вызов - он позволяет
извлекать какие-то связанные сущности
если они соответствующим образом замаппированы через ReferenceAttribute или
CrossReferenceAttribute.
Нпример метод торга может вернуть все предложения которые поступили на торг -
эти объекты хранятся в базе данных, а не в самом объекте Trade.
public void testList() throws EntityNotMappedException, WrongMethodException,
SQLException, BuildClauseException, IOException {
assertTrue("Count of trades: " + DataScooter.getDefault().list
(Trade.class.getName()).size(), (DataScooter.getDefault().list(
Trade.class.getName()).size() == 4));
assertTrue((DataScooter.getDefault().list(Offer.class.getName()).size() == 4));
assertTrue((DataScooter.getDefault().list(Contract.class.getName()).size() == 4));
assertTrue((DataScooter.getDefault().list(User.class.getName()).size() == 4));
}
Здесь мы тестируем извлечение спиcков объектов
public void testFirst() throws EntityNotMappedException, WrongMethodException,
SQLException, BuildClauseException, IOException {
assertTrue(((Trade) DataScooter.getDefault().first(Trade.class.getName(),
"userId", user1.getId())).equals(trade1));
}
Здесь мы тестируем извлечение одного первого объекта удовлетворяющего некоторым условиям
public void testSelectCross() throws EntityNotMappedException, WrongMethodException,
SQLException, BuildClauseException, IOException {
DataSnip dataSnip = DataSnip.select(Offer.class.getName());
dataSnip.join(Contract.class.getName()).join(Trade.class.getName()).where("userId", user1.getId());
assertTrue(((Offer) DataScooter.getDefault().first(dataSnip)).getId().equals(offer1.getId()));
dataSnip = DataSnip.select(Trade.class.getName());
dataSnip.join(Contract.class.getName()).join(Offer.class.getName()).where("userId", user2.getId());
List dataList = DataScooter.getDefault().list(dataSnip);
assertTrue(dataList.contains(trade2));
assertTrue(dataList.contains(trade3));
assertTrue(dataList.contains(trade4));
}
Здесь мы тестируем извлечение предложения на торг по кторому заключен контракт с торгом от пользователя
user1. Или торг, по кторому заключен контракт по предложению от пользователя user2.
public void testFetchByResult() throws EntityNotMappedException {
DataSnip dataSnip = DataSnip.select(Trade.class.getName());
dataSnip.whereId(trade1.getId());
dataSnip.fetchByResult(Contract.class.getName()).fetchByResult(Offer.class.
getName()).fetchByResult(User.class.getName());
Snip snip = DataScooter.getDefault().retrieve(dataSnip);
assertTrue("Wrong name: " + snip.getData()[0][1], snip.getData()[0][1].equals("trade1"));
assertTrue(snip.getFetch()[0].getData()[0][1].equals("text1"));
assertTrue(snip.getFetch()[0].getFetch()[0].getData()[0][1].equals("off1"));
assertTrue(snip.getFetch()[0].getFetch()[0].getFetch()[0].getData()[0][1].equals("aa"));
DataScooter.getDefault().deleteSnip(snip);
Snip snipDeleted = DataScooter.getDefault().retrieve(dataSnip);
assertTrue(snipDeleted.getData().length == 0);
DataScooter.getDefault().saveSnip(snip);
snip = DataScooter.getDefault().retrieve(dataSnip);
assertTrue(snip.getData()[0][1].equals("trade1"));
assertTrue(snip.getFetch()[0].getData()[0][1].equals("text1"));
assertTrue(snip.getFetch()[0].getFetch()[0].getData()[0][1].equals("off1"));
assertTrue(snip.getFetch()[0].getFetch()[0].getFetch()[0].getData()[0][1].equals("aa"));
}
А здесь мы ставим задвчу извлечь торг с идентификатором trade1.getId() и при этом извлечь все контракты,
заключенные по этому торгу, все предложения с которыми заключены эти контракты а так же всех пользователей
которые эти предложения сделали-
Snip snip = DataScooter.getDefault().retrieve(dataSnip);
И мы получаем всю эту иерархию объектов внутри объекта Snip - одим обращением к datascooter!
DataScooter.getDefault().deleteSnip(snip);
Теперь удаляем всю эту иерархию объектов из базы...
DataScooter.getDefault().saveSnip(snip);
А теперь снова сохраняем все эти объекты в базу и проверяем что они снова на месте - все! Никакая другая система ORM
на такое не способна!
public void testFetch() throws EntityNotMappedException {
DataSnip dataSnip = DataSnip.select(Trade.class.getName());
dataSnip.whereId(trade1.getId());
dataSnip.fetch(Contract.class.getName());
dataSnip.fetch(Offer.class.getName());
dataSnip.fetch(User.class.getName());
Snip snip = DataScooter.getDefault().retrieve(dataSnip);
assertTrue(snip.getData()[0][1].equals("trade1"));
assertTrue(snip.getFetch()[0].getData()[0].length == 4);
assertTrue(snip.getFetch()[0].getData()[1].length == 4);
assertTrue(snip.getFetch()[0].getData()[2].length == 4);
}
Здесь мы просто выбираем из базы определенный торг плюс все необходимое для удаленной работы - список контрактов,
предложений и пользователей - связанных или не связанных с этим торгом.
public void testUpdate() throws EntityNotMappedException, WrongMethodException,
SQLException, BuildClauseException, IOException {
DataScooter.getDefault().update(trade1, "goods", "Toyota");
DataScooter.getDefault().update(trade2, "goods", "BMW");
assertTrue(((Trade) DataScooter.getDefault().firstById(Trade.class.getName(),
trade1.getId())).getGoods().equals("Toyota"));
assertTrue(((Trade) DataScooter.getDefault().firstById(Trade.class.getName(),
trade2.getId())).getGoods().equals("BMW"));
}
Тестируем апдейт - изменяем значения полей двух торгов.
public void testDelete() throws EntityNotMappedException, WrongMethodException,
SQLException, BuildClauseException, IOException {
DataScooter.getDefault().delete(trade1);
DataScooter.getDefault().delete(Trade.class.getName(), "goods", "BMW");
assertTrue(DataScooter.getDefault().firstById(Trade.class.getName(),
trade1.getId()) == null);
assertTrue(DataScooter.getDefault().first(Trade.class.getName(), "goods", "BMW") == null);
}
Тестируем удаление объектов из базы
public void testECount() {
assertTrue(DataScooter.getDefault().count(Trade.class.getName()) == 2);
assertTrue(DataScooter.getDefault().count(Offer.class.getName()) == 4);
}
Подсчитываем количество объектов в базе
public void testSaveOrUpdate() throws EntityNotMappedException,
WrongMethodException, SQLException, BuildClauseException, IOException {
DataScooter.getDefault().saveOrUpdate(trade1);
trade1.setGoods("Subaru");
DataScooter.getDefault().saveOrUpdate(trade1);
assertTrue(DataScooter.getDefault().first(Trade.class.getName(),
"goods", "Subaru") != null);
}
Совершаем операцию сохранения для случаев когда не известно заранее - уже сохранялся ли такой объект раньше или нет
public void testSaveAll() throws EntityNotMappedException,
WrongMethodException, SQLException, BuildClauseException, IOException {
list.add(new Offer("List", trade1.getId(), 55L, new Date(), new Date(), user4.getId()));
list.add(new Offer("List", trade1.getId(), 55L, new Date(), new Date(), user2.getId()));
list.add(new Offer("List", trade2.getId(), 55L, new Date(), new Date(), user2.getId()));
list.add(new Offer("List", trade4.getId(), 55L, new Date(), new Date(), user2.getId()));
DataScooter.getDefault().saveAll(list);
assertTrue("Offers in the db: " + DataScooter.getDefault().list
(Offer.class.getName(), "name", "List").size(), DataScooter.getDefault().list(
Offer.class.getName(),
"name",
"List").size() == 4);
}
Проверяем сохранение списка объектов
public void testDeleteAll() throws EntityNotMappedException, WrongMethodException,
BuildClauseException, SQLException, IOException {
DataScooter.getDefault().deleteAll(list);
assertTrue(DataScooter.getDefault().list(Offer.class.getName(), "name", "List").size() == 0);
}
Проверяем удаление списка объектов
public void testPagination() throws EntityNotMappedException, WrongMethodException,
SQLException, BuildClauseException, IOException {
Date start = new Date();
list1.add(new Offer("List2", trade1.getId(), 55L, start, start, user4.getId()));
list1.add(new Offer("List2", trade1.getId(), 55L, start, start, user2.getId()));
list1.add(new Offer("List2", trade2.getId(), 55L, start, start, user2.getId()));
list1.add(new Offer("List2", trade4.getId(), 55L, start, start, user2.getId()));
list1.add(new Offer("List2", trade1.getId(), 55L, start, start, user4.getId()));
list1.add(new Offer("List2", trade1.getId(), 55L, start, start, user2.getId()));
list1.add(new Offer("List2", trade2.getId(), 55L, start, start, user2.getId()));
list1.add(new Offer("List2", trade4.getId(), 55L, start, start, user2.getId()));
DataScooter.getDefault().saveAll(list1);
assertTrue("Pagination error " + DataScooter.getDefault().list(Offer.class.getName(),
1, 2).size(), DataScooter.getDefault().list(
Offer.class.getName(),
1,
2).size() == 2);
assertTrue("Pagination error " + DataScooter.getDefault().list(Offer.class.getName(),
"name", "List2", 2, 18).size(), DataScooter
.getDefault().list(Offer.class.getName(), "name", "List2", 2, 18).size() == 6);
}
Проверяем постраничный вывод больших списков объектов
public void testGroupFetch() throws EntityNotMappedException {
DataSnip dataSnip = DataSnip.group();
dataSnip.fetch(Contract.class.getName());
dataSnip.fetch(Offer.class.getName());
dataSnip.fetch(User.class.getName());
Snip snip = DataScooter.getDefault().retrieve(dataSnip);
assertTrue(snip.getData() == null);
assertTrue(snip.getFetch()[0].getData()[0].length == 4);
assertTrue(snip.getFetch()[0].getData()[1].length == 4);
assertTrue(snip.getFetch()[0].getData()[2].length == 4);
}
А здесь проверяем извлечение произвольного набора списков объектов
public void testPipe() throws SnipManagerException, EntityNotMappedException,
SQLException, BuildClauseException, IOException {
DataScooter.getInstance().putSource(new DataSource("mysql-1",
"jdbc:mysql://localhost/testtest", "root", "root", "mysql", false));
IDataManager manager = DataScooter.getInstance().getManager("mysql-1");
manager.verifyTables(true);
TestUtils.clearAll(manager);
int count1 = DataScooter.getDefault().count(Trade.class.getName());
manager.saveSnip(DataScooter.getDefault().retrieve(DataSnip.select(Trade.class.getName())));
int count = manager.count(Trade.class.getName());
assertTrue("Size: " + count + "---" + count1, count == count1);
}
А здесь пример перекачки списка объектов из одной базы в другую
manager.saveSnip(DataScooter.getDefault().retrieve(DataSnip.select(Trade.class.getName())));
Только одна строка и осуществляет и извлечение и сохранения
public void testDataSet() throws SnipManagerException, EntityNotMappedException,
SQLException, BuildClauseException, IOException {
String entity = "someObject";
EntityBundle bundle = new EntityBundle(entity, null, "SomeTable", null, null);
bundle.setId(new SimpleAttribute("id", "id", DBType.CHAR, new Integer(36), null, Boolean.TRUE));
bundle.addSimpleAttribute(new SimpleAttribute("first", "first", DBType.STRING, new Integer(55), null, null));
bundle.addSimpleAttribute(new SimpleAttribute("second", "second", DBType.INT, null, null, null));
DataScooter.getInstance().addBundle(bundle, true);
DataScooter.getDefault().clear(entity);
int i = 5555;
String string = "atom";
DataScooter.getDefault().saveSet(entity, UUID.randomUUID().toString(), string, i);
Snip snip = DataScooter.getDefault().retrieve(DataSnip.select(entity));
assertTrue("Size: " + snip.getData().length, snip.getData().length == 1);
assertTrue("Attribute: " + snip.getData()[0][1], snip.getData()[0][1].equals(string));
BigDecimal bb = new BigDecimal(snip.getData()[0][2] + "");
assertTrue("Attribute: " + snip.getData()[0][2], bb.intValue() == i);
snip.getData()[0][1] = string + "bbb";
snip.getData()[0][2] = i + 1;
DataScooter.getDefault().updateSnip(snip);
snip = DataScooter.getDefault().retrieve(DataSnip.select(entity));
assertTrue("Size: " + snip.getData().length, snip.getData().length == 1);
assertTrue("Attribute: " + snip.getData()[0][1], snip.getData()[0][1].equals(string + "bbb"));
bb = new BigDecimal(snip.getData()[0][2] + "");
assertTrue("Attribute: " + snip.getData()[0][2], bb.intValue() == i + 1);
DataScooter.getDefault().deleteSnip(snip);
snip = DataScooter.getDefault().retrieve(DataSnip.select(entity));
assertTrue("Size not empty : " + snip.getData().length, snip.getData().length == 0);
String entity1 = "someObject1";
EntityBundle bundle1 = new EntityBundle(entity1, null, "d_trade", null, null);
bundle1.setId(new SimpleAttribute("id", "s_id", DBType.CHAR, new Integer(36), null, Boolean.TRUE));
bundle1.addSimpleAttribute(new SimpleAttribute("name", "s_name",
DBType.STRING, new Integer(36), null, null));
bundle1.addSimpleAttribute(new SimpleAttribute("goods", "s_goods", DBType.STRING, null, null, null));
bundle1.addSimpleAttribute(new SimpleAttribute("type", "s_type", DBType.INT, null, null, null));
bundle1.setDiscriminator(new Condition(new LogicValue("type", Constraint.EQUAL.name(), 0)));
DataScooter.getInstance().addBundle(bundle1, false);
Snip snip1 = DataScooter.getDefault().retrieve(DataSnip.select(entity1));
assertTrue("Size of trades substitutor: " + snip1.getData().length, snip1.getData().length == 3);
}
А этот длинный пример показывает пример использования наборов данных - то есть маппингов без объектов. То есть маппинг без указания класса
- это просто именованный набор данных из одной таблицы с которым можно делать все тоже самое что и с замаппированным
объектом - извлекать , сохранять, изменять, удалять...
String entity = "someObject";
EntityBundle bundle = new EntityBundle(entity, null, "SomeTable", null, null);
bundle.setId(new SimpleAttribute("id", "id", DBType.CHAR, new Integer(36), null, Boolean.TRUE));
bundle.addSimpleAttribute(new SimpleAttribute("first", "first",
DBType.STRING, new Integer(55), null, null));
bundle.addSimpleAttribute(new SimpleAttribute("second", "second", DBType.INT, null, null, null));
DataScooter.getInstance().addBundle(bundle, true);
EntityBundle - это и есть такой носитель маппинга - здесь мы его создаем и в последней строчке вставляем в
datascooter - теперь можно манипулировать этим объектом
DataScooter.getDefault().saveSet(entity, UUID.randomUUID().toString(), string, i);
Сохраняем набор данных - просто последовательность полей.
snip = DataScooter.getDefault().retrieve(DataSnip.select(entity));
И извлекаем его же.
public void testLogicAttributes() throws SnipManagerException,
EntityNotMappedException, SQLException, BuildClauseException, IOException {
List list2 = DataScooter.getDefault().list(ChildTrade.class.getName());
assertTrue("Subset count wrong: " + list2.size(), (list2.size() == 1));
}
А здесь мы тестируем извлечение списка потомков Trade , хранящихся в той же таблице - datascooter никогда их
не перепутает - он может извлекать их по логическому выражению(logic_condition) любой сложности
Это такой сложный дискриминатор - я нигде не видел ничего подобного. Здесь можнт быть он смотрится непонятно -
это не беда - в стандарном редакторе точек расширения эклипса вам не придется иметь дела с тегами - просто
заполняете форму! Ведь маппинг сделан на точках расширения.
Вот так это выглядит в редакторе точек расширения:
public void testSnipQuery() throws SnipManagerException, EntityNotMappedException,
SQLException, BuildClauseException, IOException {
EclipseQueryProvider provider = new EclipseQueryProvider();
provider.explore();
DataSnip query = provider.getItem("selectOffer");
assertTrue(query != null);
Snip snip = DataScooter.getDefault().retrieve(query);
assertTrue("First offer " + snip.getData()[0][1], snip.getData()[0][1].equals("off4"));
assertTrue("First contract " + snip.getFetch()[0].getData()[0][1],
snip.getFetch()[0].getData()[0][1].equals("text1")
|| snip.getFetch()[0].getData()[0][1].equals("text2")
|| snip.getFetch()[0].getData()[0][1].equals("text3")
|| snip.getFetch()[0].getData()[0][1].equals("text4"));
assertTrue("Second contract " + snip.getFetch()[0].getData()[1][1],
snip.getFetch()[0].getData()[1][1].equals("text1")
|| snip.getFetch()[0].getData()[1][1].equals("text2")
|| snip.getFetch()[0].getData()[1][1].equals("text3")
|| snip.getFetch()[0].getData()[1][1].equals("text4"));
assertTrue("First trade " + snip.getFetch()[0].getFetch()[0].getData()[0][1],
snip.getFetch()[0].getFetch()[0].getData()[0][1]
.equals("trade1")
|| snip.getFetch()[0].getFetch()[0].getData()[0][1].equals("trade3")
|| snip.getFetch()[0].getFetch()[0].getData()[0][1].equals("trade4"));
assertTrue("s" + snip.getFetch()[0].getFetch()[0].getData()[1][1],
snip.getFetch()[0].getFetch()[0].getData()[1][1].equals("trade4")
|| snip.getFetch()[0].getFetch()[0].getData()[1][1].equals("trade3")
|| snip.getFetch()[0].getFetch()[0].getData()[1][1].equals("trade1"));
}
Вот это тоже интересная штука - SnipQuery - это сделанный опять же на точках расширения механизм
записи предопределенных иерархических запросов реализующих все замечательные возможности datascooter.
Вот так это выглядит в редакторе точек расширения:
|