Для більшості java рограмістів сьогодні Hibernate == ORM. проте є ряд інших, не менш цікавих ORM, які можуть виявитися куди більш підходящим для вашого проекту, ніж улюблений Хібернейт.
Хочу звернути вашу увагу на EclipseLink, який пішов від ораклівського TopLink і сьогодні являє більш ніж підтримку персістенсу через JPA, але також JAXB, JCA та SDO (простими словами – можна працювати з XML, веб-сервісами чи но-скл базами). Але я сфокусуюся саме на ORM стороні цього рішення, і без дискутування чому ORM це погано і треба обережно совати його до свого проекту:)
Пропоную розглянути, як EclipseLink може допомогти, якщо на проекті Оракл СКБД
Отже, більшість знайомі з Хібернейтом, або з JPA in general. Хочу поділитися своїм досвідом використання EclipseLink, після інтенсивної роботи з Hibernate.
Перший плюс EclipseLink – це етелонна реалізація JPA (хоча це привносить і свої мінуси). Другий, і найбільш вагомий плюс – велика кількість Оракл-орієнтед плюшек, про які піде нижче – отже, якщо на вашому проекті Оракл і ви не плануєте підтримути якісь інші СКБД, то вам прямий шлях до використання EclipseLink, а не Хібернейт. І по-третє, статус з багами в кожному з проектів (as for July 26, 2011):
Name – Number of bugs – Number of blockers – The oldest blocker
Hibernate – 156 – 49 – 29/Sep/2006
EclipseLink – 15 – 3 – 04/Jun/2010
(!) Перед використанням слід обов”язково ознайомитися з інструкцією по використання, бо на вас чекатимуть різноманітні підводні камені. Один з найбільш підступних – це кеш другого рівня, який по замовчуваню увімкнений (це WeakHashMap)
Ну а тепер про обіцяні плюшки для Ораклу:
1) EclipseLink вміє вписувати хінти до кверів,
коли під”єднаний до Оракла, а також має АПІ який дозволяє вам самостійно додавати хінти: наприклад, з банально
1 |
Query query <span style="color: #303030;">=</span> em<span style="color: #303030;">.</span><span style="color: #0000c0;">createQuery</span><span style="color: #303030;">(</span><span style="background-color: #fff0f0;">"SELECT e FROM Employee e ORDER BY e.lastName ASC, e.firstName ASC"</span><span style="color: #303030;">);</span><br />query<span style="color: #303030;">.</span><span style="color: #0000c0;">setFirstResult</span><span style="color: #303030;">(</span><span style="color: #0000d0; font-weight: bold;">5</span><span style="color: #303030;">);</span><br />query<span style="color: #303030;">.</span><span style="color: #0000c0;">setMaxResults</span><span style="color: #303030;">(</span><span style="color: #0000d0; font-weight: bold;">5</span><span style="color: #303030;">);</span><br />List<span style="color: #303030;"><</span>Employee<span style="color: #303030;">></span> emps <span style="color: #303030;">=</span> query<span style="color: #303030;">.</span><span style="color: #0000c0;">getResultList</span><span style="color: #303030;">();</span><br /> |
Ви отримаєте ось такий SQL запит (і одразу біндінг, без додаткових налаштувань логування як у хібернейту)
1 |
<span style="color: green; font-weight: bold;">SELECT</span> <span style="color: #303030;">*</span> <span style="color: green; font-weight: bold;">FROM</span> (<span style="color: green; font-weight: bold;">SELECT</span> <span style="color: grey;">/*+ FIRST_ROWS */</span> a.<span style="color: #303030;">*</span>, ROWNUM rnum <span style="color: green; font-weight: bold;">FROM</span> (<br /> <span style="color: green; font-weight: bold;">SELECT</span> t0.EMP_ID <span style="color: green; font-weight: bold;">AS</span> EMP_ID1, t1.EMP_ID <span style="color: green; font-weight: bold;">AS</span> EMP_ID2, t0.F_NAME <span style="color: green; font-weight: bold;">AS</span> F_NAME3, t0.L_NAME <span style="color: green; font-weight: bold;">AS</span> L_NAME4, t0.START_TIME <span style="color: green; font-weight: bold;">AS</span> START_TIME5, <br /> t0.END_TIME <span style="color: green; font-weight: bold;">AS</span> END_TIME6, t0.GENDER <span style="color: green; font-weight: bold;">AS</span> GENDER7, t1.SALARY <span style="color: green; font-weight: bold;">AS</span> SALARY8, t0.<span style="color: green; font-weight: bold;">VERSION</span> <span style="color: green; font-weight: bold;">AS</span> VERSION9, t0.START_DATE <span style="color: green; font-weight: bold;">AS</span> START_DATE10, <br /> t0.END_DATE <span style="color: green; font-weight: bold;">AS</span> END_DATE11, t0.MANAGER_ID <span style="color: green; font-weight: bold;">AS</span> MANAGER_ID12, t0.ADDR_ID <span style="color: green; font-weight: bold;">AS</span> ADDR_ID13 <br /> <span style="color: green; font-weight: bold;">FROM</span> EMPLOYEE t0, SALARY t1 <br /> <span style="color: green; font-weight: bold;">WHERE</span> (t1.EMP_ID <span style="color: #303030;">=</span> t0.EMP_ID) <span style="color: green; font-weight: bold;">ORDER</span> <span style="color: green; font-weight: bold;">BY</span> t0.L_NAME <span style="color: green; font-weight: bold;">ASC</span>, t0.F_NAME <span style="color: green; font-weight: bold;">ASC</span>) a <span style="color: green; font-weight: bold;">WHERE</span> ROWNUM <span style="color: #303030;"><=</span> <span style="color: #303030;">?</span>) <span style="color: green; font-weight: bold;">WHERE</span> rnum <span style="color: #303030;">></span> <span style="color: #303030;">?</span><br /> <br />bind <span style="color: #303030;">=></span> [<span style="color: #0000d0; font-weight: bold;">10</span>, <span style="color: #0000d0; font-weight: bold;">5</span>]<br /> |
Як бачите, підказка оптимізатору самостійно з”явилася в запиті
2) Підтримка ієрархічних запитів оракла
Не певен щодо перекладу, мова йде про Hierarchical Queries http://docs.oracle.com/cd/B19306_01/server.102/b14200/queries003.htm
1 |
ReadAllQuery raq <span style="color: #303030;">=</span> <span style="color: green; font-weight: bold;">new</span> ReadAllQuery(Employee.<span style="color: green; font-weight: bold;">class</span>);<br /><span style="color: #303030;">//</span> Specifies a <span style="color: green; font-weight: bold;">START</span> <span style="color: green; font-weight: bold;">WITH</span> expression<br />Expression startExpr <span style="color: #303030;">=</span> expressionBuilder.<span style="color: green; font-weight: bold;">get</span>(<span style="color: #a06000;">"id"</span>).equal(<span style="color: green; font-weight: bold;">new</span> <span style="color: #007020;">Integer</span>(<span style="color: #0000d0; font-weight: bold;">1</span>));<br /><span style="color: #303030;">//</span> Specifies a <span style="color: green; font-weight: bold;">CONNECT</span> <span style="color: green; font-weight: bold;">BY</span> expression<br />Expression connectBy <span style="color: #303030;">=</span> expressionBuilder.<span style="color: green; font-weight: bold;">get</span>(<span style="color: #a06000;">"managedEmployees"</span>);<br /><span style="color: #303030;">//</span> Specifies an <span style="color: green; font-weight: bold;">ORDER</span> SIBLINGS <span style="color: green; font-weight: bold;">BY</span> vector<br />Vector <span style="color: green; font-weight: bold;">order</span> <span style="color: #303030;">=</span> <span style="color: green; font-weight: bold;">new</span> Vector();<br /><span style="color: green; font-weight: bold;">order</span>.addElement(expressionBuilder.<span style="color: green; font-weight: bold;">get</span>(<span style="color: #a06000;">"lastName"</span>));<br /><span style="color: green; font-weight: bold;">order</span>.addElement(expressionBuilder.<span style="color: green; font-weight: bold;">get</span>(<span style="color: #a06000;">"firstName"</span>));<br />raq.setHierarchicalQueryClause(startExpr, connectBy, <span style="color: green; font-weight: bold;">order</span>);<br />Vector employees <span style="color: #303030;">=</span> uow.executeQuery(raq);<br /> |
Буде перетворено на щось типу
1 |
<span style="color: green; font-weight: bold;">SELECT</span> <span style="color: #303030;">*</span> <span style="color: green; font-weight: bold;">FROM</span> EMPLOYEE <span style="color: green; font-weight: bold;">START</span> <span style="color: green; font-weight: bold;">WITH</span> ID<span style="color: #303030;">=</span><span style="color: #0000d0; font-weight: bold;">1</span> <span style="color: green; font-weight: bold;">CONNECT</span> <span style="color: green; font-weight: bold;">BY</span> <span style="color: green; font-weight: bold;">PRIOR</span> ID<span style="color: #303030;">=</span>MANAGER_ID <span style="color: green; font-weight: bold;">ORDER</span> SIBLINGS <span style="color: green; font-weight: bold;">BY</span> LAST_NAME, FIRST_NAME<br /> |
Безціна функціональність, якщо доводеться багато оперувати з ієрархічними структурами
3) Підтримка Oracle Flashback Transaction Query
Ніколи не використовував цей функціонал, і чесно кажу навіть не уявляю, кому він може стати в нагоді (хто знає – поділіться досвідом)
4) Альтернативний спосіб викликати stored procedures, відміний від Хібернейтівського
Тобто, в хібернейті сторед процедуру можна описати, наприклад в hbm.xml чи jpa @NamedStoredProcedureQueryтоді як на додачу до JPA-way, EclipseLink підтримує ще й StoredFunctionCall клас для динамічного формування виклику до деякої сторед процедури, а-ля:
1 |
StoredFunctionCall functionCall <span style="color: #303030;">=</span> <span style="color: green; font-weight: bold;">new</span> StoredFunctionCall();<br />functionCall.setProcedureName(<span style="color: #a06000;">"CHECK_VALID_EMPLOYEE"</span>);<br />functionCall.addNamedArgument(<span style="color: #a06000;">"EMP_ID"</span>);<br />functionCall.setResult(<span style="color: #a06000;">"FUNCTION_RESULT"</span>, String.<span style="color: green; font-weight: bold;">class</span>);<br />ValueReadQuery query <span style="color: #303030;">=</span> <span style="color: green; font-weight: bold;">new</span> ValueReadQuery();<br />query.setCall(functionCall);<br />query.addArgument(<span style="color: #a06000;">"EMP_ID"</span>);<br />List args <span style="color: #303030;">=</span> <span style="color: green; font-weight: bold;">new</span> ArrayList();<br />args.addElement(<span style="color: green; font-weight: bold;">new</span> <span style="color: #007020;">Integer</span>(<span style="color: #0000d0; font-weight: bold;">44</span>));<br />String <span style="color: green; font-weight: bold;">valid</span> <span style="color: #303030;">=</span> (String) <span style="color: green; font-weight: bold;">session</span>.executeQuery(query, args);<br /> |
Ще одна корисна штукенція, наче підтримується для всіх СКБД, це АПІ для “репортинг запитів”. Тобто, якщо вам треба заюзати в коді AVG, SUM і тд, то не треба писати SQL (чи інші його варіації) – можно просто використати ReportQuery, який дозволяє побудувати таку кверю через маніпулювання методами
1 |
ReportQuery query <span style="color: #303030;">=</span> <span style="color: green; font-weight: bold;">new</span> ReportQuery<span style="color: #303030;">(</span>Employee<span style="color: #303030;">.</span><span style="color: #0000c0;">class</span><span style="color: #303030;">,</span> emp<span style="color: #303030;">);</span><br />query<span style="color: #303030;">.</span><span style="color: #0000c0;">addMaximum</span><span style="color: #303030;">(</span><span style="background-color: #fff0f0;">"max-salary"</span><span style="color: #303030;">,</span> emp<span style="color: #303030;">.</span><span style="color: #0000c0;">get</span><span style="color: #303030;">(</span><span style="background-color: #fff0f0;">"salary"</span><span style="color: #303030;">));</span><br />query<span style="color: #303030;">.</span><span style="color: #0000c0;">addAverage</span><span style="color: #303030;">(</span><span style="background-color: #fff0f0;">"average-salary"</span><span style="color: #303030;">,</span> emp<span style="color: #303030;">.</span><span style="color: #0000c0;">get</span><span style="color: #303030;">(</span><span style="background-color: #fff0f0;">"salary"</span><span style="color: #303030;">));</span><br />query<span style="color: #303030;">.</span><span style="color: #0000c0;">addAttribute</span><span style="color: #303030;">(</span><span style="background-color: #fff0f0;">"city"</span><span style="color: #303030;">,</span> emp<span style="color: #303030;">.</span><span style="color: #0000c0;">get</span><span style="color: #303030;">(</span><span style="background-color: #fff0f0;">"address"</span><span style="color: #303030;">).</span><span style="color: #0000c0;">get</span><span style="color: #303030;">(</span><span style="background-color: #fff0f0;">"city"</span><span style="color: #303030;">));</span><br /> |
на цьому огляд важливих фіч EclipseLink можна завершити
Роблячи вибір, слід пам”ятатти що Хібернейт має власні специфічні плюшки, і слід зважити який функціонал вам буде більш потрібний