Wrong parameter binding when using named queries with optional parameters in EclipseLink - jpa

I'm trying to do a database lookup using JPA with EclipseLink. My database is Oracle 11.2.0. I have the following entity classes defined:
#Entity
#Table(name = "BS_PROVIDERS")
public class BsProvider {
#Id
#Column(name = "GUID")
private String guid;
public String getGuid() { return guid; }
public void setGuid(String guid) { this.guid = guid; }
#Column(name = "NAME")
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
#Entity
#Table(name = "CP_PROVIDERS")
public class CpProvider{
#Id
#Column(name = "GUID")
private String guid;
public String getGuid() { return guid; }
public void setGuid(String guid) { this.guid = guid; }
#Column(name = "NAME")
private String name;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
#NamedQueries({
#NamedQuery(
name = Catalog.FIND_BY_CPP_AND_BSP,
query = "select c from Catalog c where (c.cpProvider = :cpProvider) and ( (:bsProvider IS NULL) or (c.bsProvider = :bsProvider))"
)
})
#Entity
#Table(name = "CATALOGS")
public class Catalog {
public static final String FIND_BY_CPP_AND_BSP = "Catalog.findByCppAndBsp";
public static final String CP_PROVIDER_PARAM = "cpProvider";
public static final String BS_PROVIDER_PARAM = "bsProvider";
#Id
#Column(name = "GUID")
private String guid;
public String getGuid() { return guid; }
public void setGuid(String guid) { this.guid = guid; }
#Column(name = "NAME")
private String name;
public String getName() { return name; }
public void setName() { this.name = name; }
#ManyToOne
#JoinColumn(name = "CPP_GUID")
private CpProvider cpProvider;
public CpProvider getCpProvider() { return cpProvider; }
public void setCpProvider(CpProvider cpProvider) { this.cpProvider = cpProvider; }
#ManyToOne()
#JoinColumn(name = "BSP_GUIG")
private BsProvider bsProvider;
public BsProvider getBsProvider() { return bsProvider; }
public void setBsProvider(BsProvider bsProvider) { this.bsProvider = bsProvider; }
}
Code that creates the query and sets the parameters:
TypedQuery<Catalog> catalogQuery = em.createNamedQuery(Catalog.FIND_BY_CPP_AND_BSP, Catalog.class);
catalogQuery.setParameter(Catalog.CP_PROVIDER_PARAM, cpProvider);
catalogQuery.setParameter(Catalog.BS_PROVIDER_PARAM, bsProvider);
List<Catalog> catalogList = catalogQuery.getResultList();
When the variable bsProvider is NULL, all parameters are registerd are registerd correctly according to the EclipseLink log:
SELECT GUID, NAME, BSP_GUIG, CPP_GUID FROM CATALOGS WHERE ((CPP_GUID = ?) AND ((? IS NULL) OR (BSP_GUIG = ?)))
bind => [18EC0EDB-21A4-4845-960A-D5D2BDAC7B87, null, null]
Otherwise when the variable bsProvider refers to an existing entity I get the following exception:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Invalid column type
Error Code: 17004
Call: SELECT GUID, NAME, BSP_GUIG, CPP_GUID FROM CATALOGS WHERE ((CPP_GUID = ?) AND ((? IS NULL) OR (BSP_GUIG = ?)))
bind => [18EC0EDB-21A4-4845-960A-D5D2BDAC7B87, com.bssys.ebpp.dbaccess.model.BsProvider#5dbbe8df, 44E8F4BF-CFDC-49DB-AB0B-718C72D6B4EF]
As you can see the first and the third parameters are bound correctly (they are replaced by the primary key values), but the second is not. What's the reason of such a strange behavior?

Related

Spring Data JPA. Parent table data is not getting rolled back when exception occurred while inserting record in child table

I have 2 tables one to many relationship between Employee and Department table, Employee table are having column Id as PK, Name and Sal whereas Department table having column Dept_ID,Dept_Name & Dept_Loc and primary key is (Dept_ID,Dept_Name) i.e composite key and Dept_ID is foreign key ref from Employee table's Id column. The issue is when I save record in parent table i.e Employee it get saved but if in case I get exception while inserting record for child table i.e Department table,,data is not getting rolled back for EMployee table. Please help I m struggling and I am attaching my code.
public class GlEmployee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "emp_seq")
#Column(name = "EMP_ID")
private long empId;
#Column(name = "EMP_CITY")
private String empCity;
#Column(name = "EMP_NAME")
private String empName;
#Column(name = "EMP_SALARY")
private BigDecimal empSalary;
// bi-directional many-to-one association to EmpDepartment
#OneToMany(mappedBy = "glEmployee",cascade = CascadeType.ALL)
private List<EmpDepartment> empDepartments = new ArrayList<>();
public GlEmployee() {
}
public long getEmpId() {
return this.empId;
}
public void setEmpId(long empId) {
this.empId = empId;
}
public String getEmpCity() {
return this.empCity;
}
public void setEmpCity(String empCity) {
this.empCity = empCity;
}
public String getEmpName() {
return this.empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public BigDecimal getEmpSalary() {
return this.empSalary;
}
public void setEmpSalary(BigDecimal empSalary) {
this.empSalary = empSalary;
}
public List<EmpDepartment> getEmpDepartments() {
return this.empDepartments;
}
public void setEmpDepartments(List<EmpDepartment> empDepartments) {
this.empDepartments = empDepartments;
}
public EmpDepartment addEmpDepartment(EmpDepartment empDepartment) {
getEmpDepartments().add(empDepartment);
empDepartment.setGlEmployee(this);
return empDepartment;
}
public EmpDepartment removeEmpDepartment(EmpDepartment empDepartment) {
getEmpDepartments().remove(empDepartment);
empDepartment.setGlEmployee(null);
return empDepartment;
}
}
#Entity
#Table(name = "EMP_DEPARTMENT")
public class EmpDepartment implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private EmpDepartmentPK id;
#Column(name = "DEP_LOC")
private String depLoc;
public EmpDepartment(EmpDepartment id, String dep) {
}
// bi-directional many-to-one association to GlEmployee
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "DEP_ID", insertable = false, updatable = false)
private GlEmployee glEmployee;
public EmpDepartment() {
}
public EmpDepartmentPK getId() {
return this.id;
}
public void setId(GlEmployee glEmployee, String deptName) {
EmpDepartmentPK empDepartment = new
EmpDepartmentPK(glEmployee.getEmpId(), deptName);
this.id = empDepartment;
}
public String getDepLoc() {
return this.depLoc;
}
public void setDepLoc(String depLoc) {
this.depLoc = depLoc;
}
public GlEmployee getGlEmployee() {
return this.glEmployee;
}
public void setGlEmployee(GlEmployee glEmployee) {
this.glEmployee = glEmployee;
}
}
#Embeddable
public class EmpDepartmentPK implements Serializable {
// default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#Column(name = "DEP_ID")
private long depId;
#Column(name = "DEP_NAME")
private String depName;
public EmpDepartmentPK() {
}
public EmpDepartmentPK(long depId, String depName) {
super();
this.depId = depId;
this.depName = depName;
}
public long getDepId() {
return this.depId;
}
public void setDepId(long depId) {
this.depId = depId;
}
public String getDepName() {
return this.depName;
}
public void setDepName(String depName) {
this.depName = depName;
}
#Service
public class EmployeeService {
#Autowired
private EmployeeRepository employeeRepository;
#Transactional
public void createEmp() {
GlEmployee employee = new GlEmployee();
employee.setEmpCity("Pune");
employee.setEmpName("Ankush");
employee.setEmpSalary(new BigDecimal(200));
employeeRepository.save(employee);
EmpDepartment department = new EmpDepartment();
department.setId(employee, "ME");
department.setDepLoc(null);
department.setGlEmployee(employee);
employee.addEmpDepartment(department);
employeeRepository.save(employee);
}
}

JPA is'nt able to create ( persist ) an EJB

Now two days trying to fix this problem.
The problem seems to come from the DAO class.
Caused by: projet.helpdesk.dao.DAOException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.1.v20150605-31e8258): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLSyntaxErrorException: ORA-02289: sequence does not exist
Error Code: 2289
Call: SELECT SEQ_GEN_IDENTITY.NEXTVAL FROM DUAL
Query: ValueReadQuery(sql="SELECT SEQ_GEN_IDENTITY.NEXTVAL FROM DUAL")
at projet.helpdesk.dao.UserDao.creer(UserDao.java:25)
This is the entity:
package projet.helpdesk.beans;
import java.sql.Timestamp;
import javax.persistence.*;
#Entity
#Table(name = "Users")
public class Utilisateur {
#Column(name = "nom")
private String nom;
#Column(name = "prenom")
private String prenom;
#Column(name = "email")
private String email;
#Column(name = "departement")
private String dept;
#Column(name = "poste")
private String poste;
#Column(name = "agence")
private String agence;
#Column(name = "mdp")
private String mdp;
#Column(name = "type")
private String type;
#Column(name = "date_inscr")
private Timestamp date_inscr;
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY )
#Column(name = "id_emp")
private int idemp;
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public String getPrenom() {
return prenom;
}
public void setPrenom(String prenom) {
this.prenom = prenom;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
public String getPoste() {
return poste;
}
public void setPoste(String poste) {
this.poste = poste;
}
public String getAgence() {
return agence;
}
public void setAgence(String agence) {
this.agence = agence;
}
public int getIdemp() {
return idemp;
}
public void setIdemp(int id) {
this.idemp = id;
}
public String getMdp() {
return mdp;
}
public void setMdp(String mdp) {
this.mdp = mdp;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Timestamp getDate_inscr() {
return date_inscr;
}
public void setDate_inscr(Timestamp date_inscr) {
this.date_inscr = date_inscr;
}
}
EDIT: Error occurs when executing query.
This is the Stacktrace:
Caused by: java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Problem compiling [SELECT u FROM Users u WHERE u.email=:email].
[14, 19] The abstract schema type 'Users' is unknown.
[28, 35] The state field path 'u.email' cannot be resolved to a valid type.
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1616)
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.createQuery(EntityManagerWrapper.java:456)
at projet.helpdesk.dao.UserDao.trouver(UserDao.java:32)
The error comes from the method "trouver"
#Stateless
public class UserDao {
private static final String JPQL_SELECT_PAR_EMAIL = "SELECT u FROM Users u WHERE u.email=:email";
private static final String PARAM_EMAIL = "email";
This is the method "trouver"
public Utilisateur trouver( String email ) throws DAOException {
Utilisateur utilisateur = null;
Query requete = em.createQuery( JPQL_SELECT_PAR_EMAIL );
requete.setParameter( PARAM_EMAIL, email );
try {
utilisateur = (Utilisateur) requete.getSingleResult();
} catch ( NoResultException e ) {
return null;
} catch ( Exception e ) {
throw new DAOException( e );
}
return utilisateur;
}
knowing that the table Users is declared.
This is the bean Utilisateur.
#Entity
#Table(name = "Users")
public class Utilisateur {...
The message tells clearly what the problem is:
Internal Exception: java.sql.SQLSyntaxErrorException: ORA-02289: sequence does not exist
sql="SELECT SEQ_GEN_IDENTITY.NEXTVAL FROM DUAL")
The code is trying to read from a database sequence named "SEQ_GEN_IDENTITY", but this one doesn't exist.
I'm not sure why this, you have this in your code:
#GeneratedValue( strategy = GenerationType.IDENTITY )
This should tell JPA that it should use the database identity column to get the ID for the object it wants to persist.
If you don't have a specific reason to use GenerationType.IDENTITY, you should change it to GenerationType.SEQUENCE.
To do that you have to change your class to look like this:
#Id
#GeneratedValue( strategy = GenerationType.SEQUENCE )
#Column(name = "id_emp")
private int idemp;
If you are using EclipseLink (default) you have to create a database sequence named "seq_gen_sequence". If you are using Hibernate you have to create a database sequence named "hibernate_sequence".
See also:
what is the use of annotations #Id and #GeneratedValue(strategy = GenerationType.IDENTITY)? Why the generationtype is identity?
#GeneratedValue(strategy=“IDENTITY”) vs. #GeneratedValue(strategy=“SEQUENCE”)
How to choose the id generation strategy when using JPA and Hibernate

What is root path in QueryDSL? Can you explain with an example?

I have the following two entity classes: Country and Type
#Entity
#Table(name = "countries")
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id ;
#Column(name = "iso_code")
private String isoCode;
public Country() {
}
public Country(String isoCode) {
this.isoCode = isoCode;
}
public Country(int id, String isoCode) {
this.id = id;
this.isoCode = isoCode;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getIsoCode() {
return isoCode;
}
public void setIsoCode(String isoCode) {
this.isoCode = isoCode;
}
#Override
public String toString() {
return "Country{" +
"id=" + id +
", isoCode='" + isoCode + '\'' +
'}';
}
}
#Entity
#Table(name = "types")
public class Type {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "type")
private String type;
#ManyToOne
#JoinColumn(name = "country_id")
private Country country;
#ManyToOne
#JoinColumn(name = "group_id")
private Group group;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Group getGroup() {
return group;
}
public void setGroup(int priority) {
this.group = group;
}
}
I am trying to retrieve groups using the following in the repository class:
QType qType = QType.type1;
QCountry qCountry = QCountry.country;
QGroup qGroup = QGroup.group;
QGroup qGroup1 = qType.group;
JPAQuery queryGroup = new JPAQuery(em);
QueryBase queryBaseGroups = queryGroup.from(qGroup).innerJoin(qGroup1, qGroup).innerJoin(qType.country, qCountry);
However, I get the error -
java.lang.IllegalArgumentException: Undeclared path 'type1'. Add this path as a source to the query to be able to reference it.
New to JPA. What am I doing wrong here?
So this was solved by adding qType to the from function in the query.
QueryBase queryBaseGroups = queryGroup.from(qGroup, qType).innerJoin(qGroup1, qGroup).innerJoin(qType.country, qCountry);

Creating JPA entity with composite primary key with #Id from #MappedSuperclass

I have a class hierarchy for JPA entities with the base class being a MappedSuperclass which has one ID defined. I am trying to use a composite key in a subclass however that does not seem to work
My code looks like this
#MappedSuperclass
public class BaseEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
protected Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
#Entity
#EntityListeners(EntityBaseListener.class)
#Inheritance(strategy=InheritanceType.JOINED)
#Table(name = "catalog_entity")
public class BaseCatalogEntity extends BaseEntity {
#Column(name = "created_at", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
#Column(name = "updated_at", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date updatedAt;
public void setCreatedAt(Date date)
{
createdAt = date;
}
public void setUpdatedAt(Date date)
{
updatedAt = date;
}
public Date getCreatedAt() {
return createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
}
#Entity
#Table(schema = "student_catalog")
#IdClass(value = StudentCatalog.StudentCatalogPK.class)
public class StudentCatalog extends BaseCatalogEntity {
#Id
#Column(name = "name", nullable = false, length = 100)
private String name;
#Id
#Column(name = "version", nullable = false)
private Integer version;
#Column(name = "description" , length = 255)
private String description;
#Column(name = "vendor" , length = 50)
private String vendor;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getVendor() {
return vendor;
}
public void setVendor(String vendor) {
this.vendor = vendor;
}
public static class StudentCatalogPK implements Serializable {
private Long id;
private String name;
private Integer version;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
#Override
public boolean equals(Object obj) {
boolean result = false;
if(obj != null && (obj instanceof StudentCatalogPK)) {
StudentCatalogPK other = (StudentCatalogPK)obj;
result = (Objects.equals(this.id, other.id) && Objects.equals(this.name, other.name) &&
Objects.equals(this.version, other.version));
}
return result;
}
#Override
public int hashCode() {
return (27780 + (this.id != null ? this.id.hashCode() : 0) +
(this.version != null ? this.version.hashCode() : 0) +
(this.name != null ? this.name.hashCode() : 0));
}
}
}
I get the following exception:
Exception Description: Invalid composite primary key specification. The names of the primary key fields or properties in the primary key class [com.example.jpa.StudentCatalog$StudentCatalogPK] and those of the entity bean class [class com.example.jpa.StudentCatalog] must correspond and their types must be the same. Also, ensure that you have specified ID elements for the corresponding attributes in XML and/or an #Id on the corresponding fields or properties of the entity class.
I am using Eclipselink 2.5.1. Is there a way I can get this to work without changing the BaseEntity and BaseCatalogEntity classes?
It is not legal in JPA to redefine the id in subclasses. This would lead to ambiguities in the table mappings as well as in polymorphic queries.
The desire to extend the key defined in a superclass is a common issue when business keys are used for DB identity. I would advise to use only surrogate keys (like UUID) for DB identity and business keys for instance identity.
Under following conditions:
your base entity should use TABLE_PER_CLASS inheritance (and as I can see it is)
your base entity (composite key) key is of the same type as that one you want to have in your derived class (so there should be also composite key of String and Integer).
You can use #AttributeOverride annotation under class declaration, removing #Id fields from it:
#AttributeOverride(name = "id", column = #Column(name = "NAME"))
This - in result, can change column name in derived entity's table and that's the most you can acheive.
When using #MappedSuperClass, it would be advisable to make the BaseEntity Class as abstract and then extending the Base class from other Entity classes.
Cleaner approach keeping inheritance in mind and designing your application.

AnnotationException: mappedBy reference an unknown target entity property

I have one exception, which yold what I have no mapping on table. But I have this
Exeption is : \
AnnotationException: mappedBy reference an unknown target entity property: Relative.people in Person.relations
Relative entity is here:
#Entity
#Table(name = "relation")
public class Relative extends AbstractModel<UUID> implements Model<UUID> {
private UUID id;
private Person person;
private RelationTypeEnum relation;
public Relative() {
}
#Override
public void assignId() {
id = UUID.randomUUID();
}
#Override
#Id
#Column(name = "id")
public UUID getId() {
return id;
}
#ManyToOne
#JoinColumn(name="person_id", nullable=false)
public Person getPerson() {
return person;
}
#Column(name = "relation")
public RelationTypeEnum getRelation() {
return relation;
}
public void setId(UUID id) {
this.id = id;
}
public void setPerson(Person person) {
this.person = person;
}
public void setRelation(RelationTypeEnum relation) {
this.relation = relation;
}
}
And Person entity is here:
#Entity
#Table(name = "people")
public class Person extends AbstractModel<UUID> implements Model<UUID> {
private UUID id;
private String name;
private List<Relative> relations;
#Override
public void assignId() {
id = UUID.randomUUID();
}
#Override
#Id
#Column(name = "id")
public UUID getId() {
return id;
}
#Column(name = "name")
public String getName() {
return name;
}
#OneToMany(targetEntity=Relative.class, cascade=CascadeType.ALL,
mappedBy="people")
public List<Relative> getRelations() {
return relations;
}
public void setId(UUID id) {
this.id = id;
}
public void setName(String username) {
this.name = username;
}
public void setRelations(List<Relative> relations) {
this.relations = relations;
}
}
Solved.
Just changed
#Table(name = "people")
to
#Table(name = "person")
In my case there was a project which included a copy of the jar causing this issue. It was a web project which is including the jar inside its lib i.e. 2 copies of the same jar one with a different class version. Only discovered this when I physically opened the main ear and found the 2nd jar inside a web project.