@Entity public class Employee implements Serializable { @Id @GeneratedValue private Long id; @Embedded private EmployeeName employeeName; // accessor omitted
Using JPQL IN clause with composite key
TweetPosted on Sunday Oct 25, 2015 at 08:16PM in JPA
Assume we have an entity named Employee
:
And EmployeeName
:
@Embeddable public class EmployeeName implements Serializable { private String firstName; private String lastName; // accessor omitted
Using preceding entity, We want to execute following JPQL:
SELECT e FROM Employee e WHERE e.employeeName IN :employeeNames
Will it work? It works for Hibernate 4.3.11.Final but unfortunately not for EclipseLink 2.6.1.
Hibernate generates following SQL for the JPQL and the parameter of a List
contains two elements:
Hibernate: select employee0_.id as id1_0_, employee0_.firstName as firstNam2_0_, employee0_.lastName as lastName3_0_ from Employee employee0_ where employee0_.firstName=? and employee0_.lastName=? or employee0_.firstName=? and employee0_.lastName=? [Employee{id=1, employeeName=EmployeeName{firstName='Scott', lastName='Vogel'}}, Employee{id=2, employeeName=EmployeeName{firstName='Nick', lastName='Jett'}}]
EclipseLink failed to generate correct SQL for the JPQL. In such case, you need to create a JPQL by hand or Criteria API that uses each column separately (lastName
and firstName
). EclipseLink produces following Exception:
Exception in thread "main" javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.1.v20150916-55dc7c3): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: java.sql.SQLDataException: An attempt was made to get a data value of type 'BIGINT' from a data value of type 'entity.EmployeeName'. Error Code: 20000 Call: SELECT ID, FIRSTNAME, LASTNAME FROM EMPLOYEE WHERE (ID IN (?,?)) bind => [EmployeeName{firstName='Scott', lastName='Vogel'}, EmployeeName{firstName='Nick', lastName='Jett'}] Query: ReadAllQuery(referenceClass=Employee sql="SELECT ID, FIRSTNAME, LASTNAME FROM EMPLOYEE WHERE (ID IN ?)") at org.eclipse.persistence.internal.jpa.QueryImpl.getDetailedException(QueryImpl.java:382) at org.eclipse.persistence.internal.jpa.QueryImpl.executeReadQuery(QueryImpl.java:260) at org.eclipse.persistence.internal.jpa.QueryImpl.getResultList(QueryImpl.java:473) at main.Main.main(Main.java:45) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140) Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.1.v20150916-55dc7c3): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: java.sql.SQLDataException: An attempt was made to get a data value of type 'BIGINT' from a data value of type 'entity.EmployeeName'. Error Code: 20000 Call: SELECT ID, FIRSTNAME, LASTNAME FROM EMPLOYEE WHERE (ID IN (?,?)) bind => [EmployeeName{firstName='Scott', lastName='Vogel'}, EmployeeName{firstName='Nick', lastName='Jett'}] Query: ReadAllQuery(referenceClass=Employee sql="SELECT ID, FIRSTNAME, LASTNAME FROM EMPLOYEE WHERE (ID IN ?)") at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:340) at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:684) at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:560) at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:2055) at org.eclipse.persistence.sessions.server.ServerSession.executeCall(ServerSession.java:570) at org.eclipse.persistence.sessions.server.ClientSession.executeCall(ClientSession.java:258) at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:242) at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228) at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeSelectCall(DatasourceCallQueryMechanism.java:299) at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.selectAllRows(DatasourceCallQueryMechanism.java:694) at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectAllRowsFromTable(ExpressionQueryMechanism.java:2740) at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectAllRows(ExpressionQueryMechanism.java:2693) at org.eclipse.persistence.queries.ReadAllQuery.executeObjectLevelReadQuery(ReadAllQuery.java:559) at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1175) at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:904) at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1134) at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:460) at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1222) at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896) at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1857) at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1839) at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1804) at org.eclipse.persistence.internal.jpa.QueryImpl.executeReadQuery(QueryImpl.java:258) ... 7 more
Complete source code that has been used in the test can be obtained from https://github.com/lbtc-xxx/jpa-composite-in
Tags: eclipselink hibernate jpa jpql
Working example of EclipseLink static weaving
TweetPosted on Saturday Oct 24, 2015 at 03:37AM in JPA
These days I’m using EclipseLink at work. It runs on Servlet containers, unfortunately not Java EE containers at there so I have experienced some difference between them. A significant one is class weaving. The dynamic weaving is enabled by default in Java EE containers but not for Java SE environment. Weaving is a prerequisite of using some important functions such as Lazy Loading but it doesn’t work for default Java SE environment. In Java SE environment, EclipseLink requires a special prerequisite that set an agent in the time of launching JVM, or use static weaving to enable Lazy Loading.
Static weaving offers some performance benefit over Dynamic weaving because it doesn’t require runtime weaving step. I think it’s preferable so I tried it over another.
Environment
-
EclipseLink 2.6.1
-
Apache Derby 10.12.1.1
-
Oracle JDK8u60
Projects
eclipselink-entity
This project contains three simple entity classes. Dept
has many Employee
, and Employee
has one Phone
.
Dept
@Entity public class Dept implements Serializable { @Id private Long id; private String deptName; @OneToMany(mappedBy = "dept") private List<Employee> employees; // accessors omitted
Employee
@Entity public class Employee implements Serializable { @Id private Long id; @ManyToOne(fetch = FetchType.EAGER) // default @JoinColumn(nullable = false) private Dept dept; private String firstName; private String lastName; @OneToOne(mappedBy = "employee", fetch = FetchType.LAZY) // overridden by LAZY private Phone phone; // accessors omitted
Note that the relation Employee.dept
is set to EAGER
, and Employee.phone
is set to LAZY
as FetchType.
Phone
@Entity public class Phone implements Serializable { @Id @OneToOne @JoinColumn(nullable = false) private Employee employee; private String phoneNumber; // accessors omitted
persistence.xml
The persistence descriptor requires a property called eclipselink.weaving
with the value static
to enable static weaving.
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> <persistence-unit name="myPU" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <exclude-unlisted-classes>false</exclude-unlisted-classes> <shared-cache-mode>NONE</shared-cache-mode> <properties> <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver"/> <property name="javax.persistence.jdbc.url" value="jdbc:derby:memory:myDB;create=true"/> <property name="javax.persistence.jdbc.user" value="app"/> <property name="javax.persistence.jdbc.password" value="app"/> <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/> <property name="eclipselink.weaving" value="static"/> <property name="eclipselink.logging.level" value="FINE"/> <property name="eclipselink.logging.parameters" value="true"/> </properties> </persistence-unit> </persistence>
pom.xml
The static weaving will be done by a convenient Maven plugin. Just put following plugin
definition in your pom.xml
and execute mvn clean install
.
<build> <plugins> <plugin> <groupId>de.empulse.eclipselink</groupId> <artifactId>staticweave-maven-plugin</artifactId> <version>1.0.0</version> <executions> <execution> <phase>process-classes</phase> <goals> <goal>weave</goal> </goals> <configuration> <persistenceXMLLocation>META-INF/persistence.xml</persistenceXMLLocation> <logLevel>FINE</logLevel> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>org.eclipse.persistence.jpa</artifactId> <version>${eclipselink.version}</version> </dependency> </dependencies> </plugin> </plugins> </build>
eclipselink-example
This project is a client of the preceding eclipselink-entity
project. It has a Main
class which simply populates some records then fetches them.
public class Main { public static void main(String[] args) { EntityManagerFactory emf = null; try { emf = Persistence.createEntityManagerFactory("myPU"); EntityManager em = null; // Populating data try { em = emf.createEntityManager(); final EntityTransaction tx = em.getTransaction(); tx.begin(); Dept dept = new Dept(); dept.setId(1l); dept.setDeptName("Engineering"); dept.setEmployees(new ArrayList<>()); em.persist(dept); Employee emp = new Employee(); emp.setId(1l); emp.setFirstName("Jane"); emp.setLastName("Doe"); dept.getEmployees().add(emp); emp.setDept(dept); em.persist(emp); Phone phone = new Phone(); phone.setPhoneNumber("000-1111-2222"); phone.setEmployee(emp); emp.setPhone(phone); em.persist(phone); tx.commit(); } finally { if (em != null) { em.close(); } } System.out.println("<<< Populating done >>>"); try { em = emf.createEntityManager(); final Employee emp = em.find(Employee.class, 1l); System.out.println(emp.getFirstName() + " " + emp.getLastName()); // EAGER System.out.println(emp.getDept().getDeptName()); // LAZY System.out.println(emp.getPhone().getPhoneNumber()); } finally { if (em != null) { em.close(); } } } finally { if (emf != null) { emf.close(); } } } }
Here you can see the Phone
entity has lazily fetched while Dept
entity was eagerly fetched:
<<< Populating done >>> [EL Fine]: sql: 2015-10-24 03:18:23.389--ServerSession(1216590855)--Connection(1488298739)--Thread(Thread[main,5,main])--SELECT ID, FIRSTNAME, LASTNAME, DEPT_ID FROM EMPLOYEE WHERE (ID = ?) bind => [1] [EL Fine]: sql: 2015-10-24 03:18:23.408--ServerSession(1216590855)--Connection(1488298739)--Thread(Thread[main,5,main])--SELECT ID, DEPTNAME FROM DEPT WHERE (ID = ?) bind => [1] Jane Doe Engineering [EL Fine]: sql: 2015-10-24 03:18:23.413--ServerSession(1216590855)--Connection(1488298739)--Thread(Thread[main,5,main])--SELECT PHONENUMBER, EMPLOYEE_ID FROM PHONE WHERE (EMPLOYEE_ID = ?) bind => [1] 000-1111-2222
This example uses in-memory Apache Derby so you don’t need to set up any databases to execute this example. complete projects can be obtained from following GitHub repositories:
Also here’s build.gradle
example: https://github.com/lbtc-xxx/eclipselink-entity/blob/master/build.gradle
Tags: derby eclipselink jpa
Modifying persistence.xml to execute drop-and-create dynamically
TweetPosted on Monday Mar 23, 2015 at 03:19PM in JPA
I need that for Arquillian testing so I created an utility method for that.
Tags: arquillian jpa
Using JPA 2.1 AttributeConverter against Java8 LocalDate / LocalDateTime
TweetPosted on Tuesday Mar 17, 2015 at 01:50PM in JPA
I created an example project using https://weblogs.java.net/blog/montanajava/archive/2014/06/17/using-java-8-datetime-classes-jpa which ran on WildFly 8.2.0.Final (Hibernate 4.3.7) and H2 / Apache Derby database.
the whole project can be obtained from https://github.com/lbtc-xxx/jpa21converter .
You don’t need to define any additional configuration in persistence.xml
if you use converters in EE environment. it goes like this:
The converter for LocalDate between DATE
@Converter(autoApply = true) public class MyLocalDateConverter implements AttributeConverter<java.time.LocalDate, java.sql.Date> { @Override public java.sql.Date convertToDatabaseColumn(java.time.LocalDate attribute) { return attribute == null ? null : java.sql.Date.valueOf(attribute); } @Override public java.time.LocalDate convertToEntityAttribute(java.sql.Date dbData) { return dbData == null ? null : dbData.toLocalDate(); } }
The converter for LocalDateTime between TIMESTAMP
@Converter(autoApply = true) public class MyLocalDateTimeConverter implements AttributeConverter<java.time.LocalDateTime, java.sql.Timestamp> { @Override public java.sql.Timestamp convertToDatabaseColumn(java.time.LocalDateTime attribute) { return attribute == null ? null : java.sql.Timestamp.valueOf(attribute); } @Override public java.time.LocalDateTime convertToEntityAttribute(java.sql.Timestamp dbData) { return dbData == null ? null : dbData.toLocalDateTime(); } }
Entity class
@Entity public class MySimpleTable implements Serializable { @Id @GeneratedValue private Long id; private java.time.LocalDateTime someLocalDateTime; private java.time.LocalDate someLocalDate; ...
Hibernate produces the DDL against H2 as follows:
create table MySimpleTable ( id bigint not null, someLocalDate date, someLocalDateTime timestamp, primary key (id) )
Using converters with @EmbeddedId
Converters doesn’t work with fields that annotated as @Id
(see http://stackoverflow.com/questions/28337798/hibernate-fails-to-load-jpa-2-1-converter-when-loaded-with-spring-boot-and-sprin ) but works with @EmbeddedId
class.
Entity class:
@Entity public class MyCompositeKeyTable implements Serializable { @EmbeddedId private MyCompositeKeyEmbeddable key; ...
Embeddable class:
@Embeddable public class MyCompositeKeyEmbeddable implements Serializable { @Column(nullable = false) private java.time.LocalDateTime someLocalDateTime; @Column(nullable = false) private java.time.LocalDate someLocalDate; ...
Produced DDL:
create table MyCompositeKeyTable ( someLocalDate date not null, someLocalDateTime timestamp not null, primary key (someLocalDate, someLocalDateTime) )