Статьи

Постоянство полиглота: EclipseLink с MongoDB и Derby

Упорство Полиглота было в новостях с некоторого времени. Начатый знаменитой записью Фаулера с конца 2011 года, я вижу еще более интересные идеи. Последним был внутренний студенческий проект компании, в котором мы использовали Scala в качестве бэкэнда, сохраняющего данные в MongoDB, Derby и Solar. Я не большой поклонник Scala и вспомнил растущую поддержку EclipseLink для баз данных NoSQL. Учитывая, что я просто должен был попробовать это.

Когда начать?

Самая большая проблема — недостающие примеры. Вы найдете совсем немного о том, как изменить контейнеры данных (NoSQL или RDBMS) с EclipseLink, но вы не найдете ни одного, который бы точно использовал обе технологии без проблем. Благодаря Шону Смиту и Гуннару Вагенкрнехту у нас есть замечательная беседа на JavaOne о постоянстве Polyglot: EclipseLink JPA для NoSQL, Relational и Beyond, которая говорит именно об этом. К сожалению, источники до сих пор нигде не были переданы, и мне пришлось восстановить это из беседы. Так, кредиты идут для Шона и Гуннара для этого.

Волшебное решение называется « Персональная композиция» . Вам нужен один блок персистентности для каждого контейнера данных. Это выглядит как следующий основной пример. У вас есть пара сущностей в каждом PU, и композитный PU является зонтиком.

Поехали

Вы должны иметь MongoDB на месте, прежде чем вы начнете этот маленький учебник пример. Запустите NetBeans и создайте два Java-проекта. Давайте назовем их polyglot-persistence-nosql-pu и polyglot-persistence -ational-pu. Поместите следующие объекты в nosql-pu: Customer, Address, Order и OrderLine. (В основном взяты из
EclipseLink nosql examples ) и поместите сущность Product в рациональный-pu.

Отдельные продукты поступают в Дерби, а все остальные объекты сохраняются в MongoDB. Интересная часть, где OrderLine имеет отношение один к одному с продуктом:

1
2
@OneToOne(cascade = {CascadeType.REMOVE, CascadeType.PERSIST})
private Product product;

Это точка, где оба мира объединяются. Подробнее об этом позже.

Оба PU должны иметь тип транзакции = ‘RESOURCE_LOCAL’ и должны содержать следующую строку в файле persistence.xml:

1
<property name='eclipselink.composite-unit.member' value='true'/>

Не забудьте добавить конкретную конфигурацию БД. Для MongoDB это

1
2
3
<property name='eclipselink.nosql.property.mongo.port' value='27017'/>
<property name='eclipselink.nosql.property.mongo.host' value='localhost'/>
<property name='eclipselink.nosql.property.mongo.db' value='mydb'/>

Для дерби это примерно так:

1
2
3
4
<property name='javax.persistence.jdbc.url' value='jdbc:derby://localhost:1527/mydb'/>
<property name='javax.persistence.jdbc.password' value='sa'/>
<property name='javax.persistence.jdbc.driver' value='org.apache.derby.jdbc.ClientDriver'/>
<property name='javax.persistence.jdbc.user' value='sa'/>

Теперь нам нужно что-то, чтобы связать эти два PU вместе. Комбинированный pu находится в образце модуля polyglot-persistence-web и выглядит так:

1
2
3
4
5
6
7
8
9
<persistence-unit name='composite-pu' transaction-type='RESOURCE_LOCAL'>
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
     <jar-file>\lib\polyglot-persistence-rational-pu-1.0-SNAPSHOT.jar</jar-file>
     <jar-file>\lib\polyglot-persistence-nosql-pu-1.0-SNAPSHOT.jar</jar-file>
      <properties>
            <property name='eclipselink.composite-unit' value='true'/>
        </properties>
</persistence-unit>
</persistence>

Не упустите путь к файлу jar. Мы собираемся упаковать это в военный архив, и поэтому nosql-pu иational-pu перейдут в папку WEB-INF / lib. Как видите, мой пример — сборка с maven. Убедитесь, что вы используете последнюю зависимость EclipseLink. Даже GlassFish 3.1.2.2 все еще поставляется с более низкой версией. Поддержка MongoDB была добавлена ​​начиная с 2.4.

1
2
3
4
5
<dependency>
 <groupId>org.eclipse.persistence</groupId>
           <artifactId>eclipselink</artifactId>
           <version>2.4.1</version>
       </dependency>

Кроме того, вам также необходимо перевернуть загрузчики классов GlassFish:

1
<class-loader delegate='false'/>

Не беспокойся о деталях. Я все ставлю на
github.com/myfear так что вы можете сами в дальнейшем разобраться в полном примере.

Тестирование это

Давайте сделаем несколько очень кратких испытаний с ним. Создайте симпатичный маленький сервлет Демо и вставьте в него составной пу. Создайте EntityManager из него и получите транзакцию. Теперь начните создавать продукты, клиента, заказ и отдельные строки заказа. Все просто JPA. Больше никакой магии здесь:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@PersistenceUnit(unitName = 'composite-pu')
 private EntityManagerFactory emf;
 
protected void processRequest() // [...]
  {
 
     EntityManager em = emf.createEntityManager();
     em.getTransaction().begin();
     // Products go into RDBMS
     Product installation = new Product('installation');
     em.persist(installation);
 
     Product shipping = new Product('shipping');
     em.persist(shipping);
 
     Product maschine = new Product('maschine');
     em.persist(maschine);
 
     // Customer into NoSQL
     Customer customer = new Customer();
     customer.setName('myfear');
     em.persist(customer);
     // Order into NoSQL
     Order order = new Order();
     order.setCustomer(customer);
     order.setDescription('Pinball maschine');
 
     // Order Lines mapping NoSQL --- RDBMS
     order.addOrderLine(new OrderLine(maschine, 2999));
     order.addOrderLine(new OrderLine(shipping, 59));
     order.addOrderLine(new OrderLine(installation, 129));
 
     em.persist(order);
     em.getTransaction().commit();
     String orderId = order.getId();
     em.close();

Если вы установите правильные свойства ведения журнала, вы увидите, что происходит:

Пара последовательностей присваивается созданным объектам Product (GeneratedValue). Сущность Customer сохраняется в Mongo с MappedInteraction. Объекты отображаются на коллекции в MongoDB.

1
2
3
4
5
6
FINE: Executing MappedInteraction()
spec => null
properties => {mongo.collection=CUSTOMER, mongo.operation=INSERT}
input => [DatabaseRecord(
CUSTOMER._id => 5098FF0C3D9F5D2CCB3CFECF
CUSTOMER.NAME => myfear)]

После этого вы видите, что продукты вставляются в Derby и снова MappedInteraction, который перетаскивает Заказ в MongoDB. Действительно крутая часть внизу на линии заказов:

01
02
03
04
05
06
07
08
09
10
ORDER.ORDERLINES => [DatabaseRecord(
 LINENUMBER => 1
 COST => 2999.0
 PRODUCT_ID => 3), DatabaseRecord(
 LINENUMBER => 2
 COST => 59.0
 PRODUCT_ID => 2), DatabaseRecord(
 LINENUMBER => 3
 COST => 129.0
 PRODUCT_ID => 1)]

У строк заказа есть объект, у которого есть product_id, который был создан для связанных сущностей продукта. В дальнейшем вы также можете найти соответствующий Заказ и перебрать товары и получить их описания:

1
Order order2 = em.find(Order.class, orderId);
1
2
3
for (OrderLine orderLine : order2.getOrderLines()) {
          String desc = orderLine.getProduct().getDescription();
          }

Хорошая маленькая демонстрация выглядит так:

Спасибо Шон, спасибо Гуннар за этот хороший маленький пример. Теперь зайдите на github.com/myfear и запачкайте руки 🙂

Ссылка: Постоянство Polyglot: EclipseLink с MongoDB и Derby от нашего партнера по JCG Маркуса Эйзела (Markus Eisele) из блога Enterprise Software Development с Java .