Eclipselink 2.5.2 JPA NPE querying elementcollection on glassfish 4 - jpa

I'm facing a strange problem porting a working application from JEE6 (glassfish 3/eclipselink 2.5.1) to JEE7 (glassfish 4/eclipselink 2.5.2).
I've these entities (getter/setter are project Lombok annotations):
#Entity
#Table(name = "languages")
public class Language {
#Getter
#Setter
#NotNull
#Column(name = "code")
private String code;
#Getter
#Setter
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "translation_id")
private Translation translations = new Translation();
}
#Entity
#Table(name = "translations")
public class Translation {
#ElementCollection
#MapKeyColumn(name="locale")
#Column(name="description")
#CollectionTable(name="translations_values")
private Map<String, String> strings = new HashMap<>();
public Translation() {
}
public Translation(Map<String, String> map) {
this.strings = map;
}
public void setString(String locale, String text) {
strings.put(locale, text);
}
public String getString(String locale) {
String returnValue = strings.get(locale);
return (returnValue != null ? returnValue : null);
}
}
If I run this JPQL query, it works:
select o from Language o join o.translations t join t.strings s where key(s) = 'it' and value(s) = 'Italiano'
If I run the same query adding an order by clause on elementcollection, it doesn't work:
select o from Language o join o.translations t join t.strings s where key(s) = 'it' and value(s) = 'Italiano' order by value(s)
The result is:
Local Exception Stack:
Exception [EclipseLink-6168] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.QueryException
Exception Description: Query failed to prepare, unexpected error occurred: [java.lang.NullPointerException].
Internal Exception: java.lang.NullPointerException
Query: ReadAllQuery(referenceClass=Language jpql="select o from Language o join o.translations t join t.strings s where key(s) = 'it' and value(s) = 'Italiano' order by value(s)")
at org.eclipse.persistence.exceptions.QueryException.prepareFailed(QueryException.java:1589)
at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:680)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.checkPrepare(ObjectLevelReadQuery.java:901)
at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:613)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:194)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:116)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:102)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:86)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1603)
Caused by: java.lang.NullPointerException
at org.eclipse.persistence.mappings.ForeignReferenceMapping.getOrderByNormalizedExpressions(ForeignReferenceMapping.java:2456)
at org.eclipse.persistence.internal.expressions.SQLSelectStatement.normalizeOrderBy(SQLSelectStatement.java:1614)
at org.eclipse.persistence.internal.expressions.SQLSelectStatement.normalize(SQLSelectStatement.java:1403)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.buildNormalSelectStatement(ExpressionQueryMechanism.java:549)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.prepareSelectAllRows(ExpressionQueryMechanism.java:1720)
at org.eclipse.persistence.queries.ReadAllQuery.prepareSelectAllRows(ReadAllQuery.java:813)
at org.eclipse.persistence.queries.ReadAllQuery.prepare(ReadAllQuery.java:744)
at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:661)
... 9 more
My application on glassfish 3 / eclipselink 2.5.1 works fine on the JPQL query above.
Is there any workaround ?
Many thanks in advance

Related

JPA SQLResultSetMapping for SQL Aliases not Working At All?

Native SQL with aliased field names + remapping to receive managed entities is required for more complex queries with joined tables.
However, the mapping of the SQL aliases leads to an exception where the aliased fields cannot be found. Can anybody detect an error in the code below, or is SQLResultSetMapping broken? (The sample below is intentionally simple to allow quick checking)
RDBMS H2, DDL
create table A(
ID INTEGER DEFAULT NOT NULL AUTO_INCREMENT PRIMARY KEY,
VAL VARCHAR(10)
);
insert into A (val) values ('val1');
insert into A (val) values ('val2');
Java class
#Entity
#NamedNativeQuery(name = "queryall",
query="select ID as AID, val from A",
resultSetMapping = "mapping")
#SqlResultSetMapping(name = "mapping",
entities = #EntityResult(
entityClass = A.class,
fields = {#FieldResult(name = "ID", column = "AID")})
)
public class A implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Integer id;
#Column(name = "VAL")
private String val;
public A() {
}
public A(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getVal() {
return val;
}
public void setVal(String val) {
this.val = val;
}
#Override
public String toString() {
return "entities.A[ id=" + id +", val="+val+ " ]";
}
public static void main(String[] args) {
EntityManagerFactory entityManagerFactory =
Persistence.createEntityManagerFactory("JavaApplication6PU");
EntityManager em = entityManagerFactory.createEntityManager();
Query sqlQuery = em.createNamedQuery("queryall");
List list = sqlQuery.getResultList();
for (Iterator<A> iterator = list.iterator(); iterator.hasNext();) {
a = iterator.next();
System.out.println(String.format("entity %s, managed: %s", a, em.contains(a)));
}
}
}
Execution stops with exception:
[EL Warning]: 2018-01-12 21:45:42.748--UnitOfWork(1823014131)--Exception
[EclipseLink-6044] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd):
org.eclipse.persistence.exceptions.QueryException
Exception Description: The primary key read from the row [DatabaseRecord(
A.ID => null
A.VAL => val1)] during the execution of the query was detected to be null.
Primary keys must not contain null.
Query: ResultSetMappingQuery(name="queryall" referenceClass=A sql="select ID as AID, val from A")
This, in other words, means: No mapping has taken place -> aliased fields not found
The same when the mapping is announced in adhoc Queries.
Query sqlQuery = em.createNativeQuery("select ID as AID, val from A","mapping");
If resultClass is used instead of resultSetMapping and no SQL aliases exist, the output is as it should be. (This proves that there is no misspelling of fields or any other error)
#NamedNativeQuery(name = "queryall",
query="select ID, val from A",
resultClass = A.class)
Output:
entity entities.A[ id=1, val=val1 ], managed: true
entity entities.A[ id=2, val=val2 ], managed: true

spring data jpa for multiple joined table

I have two tables: ProductUsage and Learner. ProductUsage have field Learner, Learner has fields id and guid. now I need to create a query to pull out all productUsage whose learner guid is in specified user ids:
SQL:
select * from product_usage
inner join learner
on product_usage.learner_id = learner.id
where
learner.guid in ("1234", "2345")
domain class:
#Data
#NoArgsConstructor
#Entity
#Table(name = "core_product_usage_increments")
public class ProductUsage {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#ManyToOne
#JoinColumn(name = "learner_id", nullable = false)
private Learner learner;
#ManyToOne
#JoinColumn(name = "learning_language_id", nullable = false)
private Language language;
}
#Data
#NoArgsConstructor
#Entity
#Table(name = "learners")
public class Learner {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "user_guid", nullable = false, unique = true)
private String guid;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
}
and repository class:
#Repository
public interface ProductUsageRepository extends CrudRepository<ProductUsage, Integer> {
#Query("SELECT p FROM ProductUsage p WHERE p.learnerGuid = :learnerGuid")
List<ProductUsage> findByLearnerGuid(String learnerGuid);
}
client class that call the repository
#Component
public class MyClient {
#Autowired
private ProductUsageRepository repository;
public MyClient(ProductUsageRepository repository) {
this.repository = repository;
}
public List<ProductUsage> getProductUageByLeanrerGuid(String learnerGuid) {
return repository.findByLearnerGuid(learnerGuid);
}
}
and my test:
#Test
public void testClient() throws Exception {
MyClient client = new MyClient(repository);
List<ProductUsage> results = client.getProductUageByLeanrerGuid("1234");
assertNotNull(result);
}
and it failed:
Caused by: java.lang.IllegalArgumentException: org.hibernate.QueryException: could not resolve property: learnerGuid of: com.acme.domain.spectrum.ProductUsage [SELECT p FROM com.acme.domain.spectrum.ProductUsage p WHERE p.learnerGuid = :learnerGuid]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1364)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1300)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:294)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
it cannot recognize the 'learnerGuid' field in ProductUsage, but that's actually defined in Learner class. how can I perform the query that join multiple tables?
ProductUsage has no learnerGuid property, only learner. Try
#Query("SELECT p FROM ProductUsage p WHERE p.learner.guid = :learnerGuid")
If that doesn't work, I have another tip:
#Query("SELECT p FROM ProductUsage p join p.Learner l WHERE l.guid = :learnerGuid")
You do not have use a #query like you did
#Query("SELECT p FROM ProductUsage p WHERE p.learnerGuid = :learnerGuid")
List<ProductUsage> findByLearnerGuid(String learnerGuid);
Spring JPA framework can build the query by method name itself. Try this
List<ProductUsage> findByLearnerGuid(String learnerGuid);
or
List<ProductUsage> findByLearner_guid(String learnerGuid);
as you have a relation to Learner from ProductUsage the findBy method can traverse through the related tables and their fields. "_" gives the framework a clear indication that query by joining the Learner table where guid =?
Otherwise the framework tries below two combinations:
where learnerGuid=?
join learner where guid=?

Using #OneToOne with Cascade.DELETE in embedded type

In an application I use EclipseLink 2.4.1 with Java Persistence 2.0.4.
I have a OneToOne mapping in an embedded class. Everything works fine, except deleting. When I try to delete the object containing the embedded class, the following exception occurs. I checked and I am not calling remove on the embedded object by myself somewhere in the code. Does anybody knows how to avoid this error or how to get around it?
Exception [EclipseLink-6002] (Eclipse Persistence Services - 2.4.1.v20121003-ad44345): org.eclipse.persistence.exceptions.QueryException
Exception Description: Aggregated objects cannot be written/deleted/queried independently from their owners.
Descriptor: [RelationalDescriptor(org.openlca.web.model.ProcessModelInfo --> [])]
Query: DeleteObjectQuery(org.openlca.web.model.ProcessModelInfo#77cc2975)
at org.eclipse.persistence.exceptions.QueryException.aggregateObjectCannotBeDeletedOrWritten(QueryException.java:240)
at org.eclipse.persistence.queries.ObjectLevelModifyQuery.prepare(ObjectLevelModifyQuery.java:205)
at org.eclipse.persistence.queries.DeleteObjectQuery.prepare(DeleteObjectQuery.java:327)
at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:614)
at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:575)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:820)
at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:751)
at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108)
at org.eclipse.persistence.queries.DeleteObjectQuery.executeInUnitOfWorkObjectLevelModifyQuery(DeleteObjectQuery.java:119)
at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2875)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1602)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1584)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1535)
at org.eclipse.persistence.queries.DeleteObjectQuery.executeDatabaseQuery(DeleteObjectQuery.java:194)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:852)
at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:751)
at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108)
at org.eclipse.persistence.queries.DeleteObjectQuery.executeInUnitOfWorkObjectLevelModifyQuery(DeleteObjectQuery.java:119)
at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2875)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1602)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1584)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1535)
at org.eclipse.persistence.internal.sessions.CommitManager.deleteAllObjects(CommitManager.java:334)
at org.eclipse.persistence.internal.sessions.CommitManager.deleteAllObjects(CommitManager.java:288)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1422)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitToDatabase(RepeatableWriteUnitOfWork.java:634)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1509)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:266)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1147)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commitInternal(EntityTransactionImpl.java:84)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:63)
at org.project.ProcessDao.delete(ProcessDao.java:41)
The relevant class snippets look like this (Process and LongText are added in the persistence.xml) - The error occurs when trying to delete a process:
Entity Class Process
#Entity
public class Process {
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "process_seq")
#Column(name = "id")
private long id;
....
#Embedded
private ProcessModelInfo modelInfo = new ProcessModelInfo();
....
}
Embedded Class ProcessModelInfo
#Embeddable
public class ProcessModelInfo {
...
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name= "f_modelling_constants")
private LongText modellingConstants = new LongText();
...
}
Entity Class LongText
#Entity
#Table(name = "tbl_long_texts")
public class LongText {
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "long_text_seq")
#Column(name = "id")
private long id;
#Lob
#Column(name = "text")
private String text;
....
}
The ProcessDao.delete method looks like this:
#Override
public void delete(Process entity) throws Exception {
if (entity == null)
return;
EntityManager em = createManager();
try {
em.getTransaction().begin();
em.remove(em.merge(entity));
em.getTransaction().commit();
} finally {
em.close();
}
}
I can't see how this would occur, but if you can create a reproducible test case, please log a bug.
Check that you don't have any events that may be call remove on the embeddable.
Try debugging or set logging level to finest.
You may want to try the 2.5 release, as it may have been fixed (although I don't see any changes in the code).

JPA : #SequenceGenerator is not generating the sequence

I my application I have used JPA with Hibernate vendor and Oracle 11G DB.
Here I am using the native query as follow on my MST_EMP table ..
Query query = this.entityManager.createNativeQuery("INSERT INTO MST_EMP emp (" +
"EMP_NAME,EMP_MAIL_ID) VALUES ('dasdas',?)");
query.setParameter(1,"dhrumil");
query.executeUpdate();
Here is my MST_EMP entity details..
#Table(name = "MST_EMP")
public class MstEmp implements Serializable, IsEntity {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "EMP_CODE")
#SequenceGenerator( name = "EMP_CODE_SEQ", sequenceName = "EMP_CODE_SEQ")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "EMP_CODE_SEQ")
private String empCode;
#Column(name="EMP_MAIL_ID")
private String empMailId;
#Column(name="EMP_NAME")
private String empName;
public MstEmp() {
}
public String getEmpCode() {
return this.empCode;
}
public void setEmpCode(String empCode) {
this.empCode = empCode;
}
public String getCreatedBy() {
return this.createdBy;
}
public void setEmpMailId(String empMailId) {
this.empMailId = empMailId;
}
public String getEmpName() {
return this.empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
}
As per my understanding, we dont need to give value to EMP_CODE in the native query. Because sequence is associated with it.
But this query gives me error like this ..
SEVERE: ORA-01400: cannot insert NULL into ("PERK"."MST_EMP"."EMP_CODE")
SEVERE: javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute native bulk manipulation query
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1179)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1112)
Can any one tell me, Do we need to provide EMP_CODE in the native query ?
Will native query , do not refer the sequence automatically declared in the Entity?
Thanks.
JPA only generates the sequence automatically when you persist a new object via EntityManager.persist() method:
E.g.
EntityManager em = \\ ... Initialise
MstEmp newMstEmp = new MstEmp();
newMstEmp.setEmpCode(...);
newMstEmp.setEmpMailId(...);
newMstEmp.setEmpName(...);
EntityTransaction tx = em.getTransaction();
tx.begin();
em.persist(newMstEmp);
tx.commit();
When you apply JPQL directly to the DB (via entityManager.createQuery()) or raw SQL directly to the DB (via entityManager.createNativeQuery() - as you do here), you must insert your own sequence:
Query query = this.entityManager.createNativeQuery("INSERT INTO MST_EMP emp (" +
"EMP_CODE,EMP_NAME,EMP_MAIL_ID) VALUES (EMP_CODE_SEQ.nextval,'dasdas',?)");
query.setParameter(1,"dhrumil");
query.executeUpdate();

JPQL NamedQuery: Access attribute of an #Embeddable class from an #ElementCollection reference

The follwing named query
<named-query name="fix.getByProblem">
<query>
SELECT f
FROM Fix f JOIN f.solved s
WHERE s.id IN :ids
</query>
</named-query>
is supposed to return all fixes that solve at least one of the given problems, but fails with the error message
Exception Description: Error compiling the query [fix.getByProblem]:
SELECT f FROM Fix f JOIN f.solved s WHERE s.id IN :ids
], unknown state or association field [id] of class [ProblemHandle].
The model is as follows: (simplified)
Fix.java
#ElementCollection
#CollectionTable(name = "FIX_SOLVED", schema = SCHEMA_NAME, joinColumns = {#JoinColumn(name = "SOURCE_VERSION", referencedColumnName = "version")})
#AttributeOverrides({ #AttributeOverride(column = #Column(name = "SOLVED_ID", nullable = true), name = "id") })
private Collection<ProblemHandle> solved;
ProblemHandle.java
#Embeddable
#Access(AccessType.PROPERTY)
public class ProblemHandle {
private Long id;
...
}
Problem.java
#Entity(name = Problem.ENTITY_NAME)
#Access(value = AccessType.FIELD)
#Table(name = Problem.TABLE_NAME, schema = Problem.SCHEMA_NAME)
#IdClass(ProblemHandle.class)
public class Problem {
public static final String ENTITY_NAME = "problem";
public static final String SCHEMA_NAME = "X";
public static final String TABLE_NAME = "PROBLEM";
#Id
#Column(name="id", nullable = false)
private Long id;
...
}
How can I achieve that without having to change the pattern, e.g. using handles?
You have #Access(AccessType.PROPERTY), so the name of your attribute comes from your get method, not the variable. What is the name of your get method?
Try removing #Access(AccessType.PROPERTY)
Also, what version are you using? Try using the 2.4 release.