HQL:Implicit Association joining not working - java

I am using Hibernate 3.2.5 for my application.
I have a Dept table and an Employees table.
Dept.java
private int deptId;
private String deptName;
private Map empMap = new HashMap();
//Getters and Setters
Employees.java
private int empId;
private String empName;
private int deptId;
private int age;
private String sex;
private Dept dept;
//Getters and Setters
Association between these two:
<map name="empMap" inverse="false" cascade="all">
<key column="DEPT_ID"></key>
<map-key formula="EMP_ID" type="integer"></map-key>
<one-to-many class="com.jdbc.Employees"/>
</map>
When I try the below statement:
Query hqlQuery = session.createQuery("from Dept dept where dept.empMap.empName = 'XYZ'");
I am getting the below exception:
org.hibernate.QueryException: illegal attempt to dereference collection [dept0_.DEPT_ID.empMap] with element property reference [empName] [from com.jdbc.Dept dept where dept.empMap.empName = 'XYZ']
Kindly let me know how to use the implicit join here. Reading the doc, I am not able to figure out what I am missing.

You are trying to access a collection like a property. You can do this instead:
from Dept dept inner join dept.empMap emp where emp.empName = 'XYZ'

Related

Eclipselink tries to inset null value in table

I'm currently trying to create a many to many mapping using eclipselink. Please note that for this specific example no table to resolve the relation is used (I kwon that this is not a good practice but it is necassary for this specific example).
I've created a database schema and the tables employee3 and insurance to map. The employee table holds a column called insurance_id which is part of the primary key in order to create the mapping. Same goes for insurance with employee_id.
Now for the code:
Here is the code for the two classes:
First off Employee:
#Entity
#Table(name="employee3", schema = "many_to_many")
public class EmployeeManyToMany
{
protected EmployeeManyToMany()
{
}
#Id
private int id;
private String firstName;
private String lastName;
#ManyToMany(cascade = CascadeType.PERSIST)
private Collection<InsuranceManyToMany> insurance;
public EmployeeManyToMany(int id, String firstName, String lastName)
{
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
insurance = new ArrayList<InsuranceManyToMany>();
}
.....
And Insurance:
#Entity
#Table(name = "insurance", schema = "many_to_many")
public class InsuranceManyToMany
{
#Id
#Column(name = "id")
private int insuranceId;
private String company;
#ManyToMany(mappedBy = "insurance", cascade = CascadeType.PERSIST)
private Collection<EmployeeManyToMany> employee;
protected InsuranceManyToMany()
{
}
public void addEmployee(EmployeeManyToMany emp)
{
employee.add(emp);
}
public InsuranceManyToMany(String company, int insuranceId)
{
this.insuranceId = insuranceId;
this.company = company;
employee = new ArrayList<EmployeeManyToMany>();
}
....
After I create an Employee object and add a list of insurances to it i try to persist it into the database.
Which results in the following error:
javax.persistence.RollbackException: Exception [EclipseLink-4002]
Eclipse Persistence Services - 2.7.3.v20180807-4be1041):
org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: null value
in column "insurance_id" violates not-null constraint
Detail: Failing row contains (1, hans, test, null).
Error Code: 0
Call: INSERT INTO many_to_many.employee3 (ID, FIRSTNAME, LASTNAME) VALUES
(?, ?, ?)
bind => [3 parameters bound]
Query: InsertObjectQuery(swd_ws18_06_Tag3.EmployeeManyToMany#88d98e)
I have no idea why this occures since the values are never null.
Any help is appreciated!
BR
Simon

Hibernate - how to selectively fetch associated properties?

I'm using Spring with hibernate.
The object I'd like to fetch is of class A, which has attribute - a set of object of class B, like
public class A {
private Integer aID;
private Set<B> bs;
private String fieldA1;
private String fieldA2;
// setters and getters
}
public class B {
private Integer bID;
private String fieldB1;
private String fieldB2;
// setters and getters
}
In the mapping file, within the class A mapping tag, I include,
<set name="bs" table="TABLE_B">
<key column="A_ID" />
<one-to-many class="com.proj.test.B"/>
</set>
Now I want to fetch the A object with the bs inside filtered with criteria that depends on value of fieldB1 and fieldB2. (not to fetch all B objects)
Any suggestions / answers?
Try out the following :
#Query(value = "Select a from A a where a.bs.fieldB1 YOUR_CONDITION")
List<A> findAWithFilteredB();

Trying Derived Identifiers using #JoinedColumns, got error Attempt to modify an identity column

This is my sql table structure:
create table TBL_EMPLOYEE_FIVE(
EMP_ID integer generated always as identity(start with 50, increment by 4),
NAME varchar(50),
COUNTRY varchar(50),
MGR_ID integer,
MGR_COUNTRY varchar(50),
constraint PK_COMPOSIT_001AD primary key(EMP_ID,COUNTRY),
constraint FK_COMPO_00123 foreign key(MGR_ID,MGR_COUNTRY) references TBL_EMPLOYEE_FIVE
)
And this is my entity mapping:
#Entity
#Table(name="TBL_EMPLOYEE_FIVE")
#IdClass(EmployeeId.class)
public class EmployeeOne implements Serializable{
public EmployeeOne(){}
public EmployeeOne(String employeeName,String empCountry){
this.empCountry = empCountry;
this.employeeName = employeeName;
}
public EmployeeOne(String employeeName,String empCountry,EmployeeOne manager){
this.empCountry = empCountry;
this.employeeName = employeeName;
this.manager = manager;
}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="EMP_ID")
private Integer employeeId;
#Id
#Column(name="COUNTRY")
private String empCountry;
#Column(name="NAME")
private String employeeName;
#ManyToOne( cascade= {CascadeType.PERSIST, CascadeType.PERSIST},
fetch= FetchType.LAZY,
targetEntity=EmployeeOne.class)
#JoinColumns({
#JoinColumn(name="MGR_ID",referencedColumnName="EMP_ID"),
#JoinColumn(name="MGR_COUNTRY",referencedColumnName="COUNTRY")
})
private EmployeeOne manager;
#OneToMany(cascade={CascadeType.PERSIST, CascadeType.PERSIST},mappedBy="manager")
private Set<EmployeeOne> employees;
// getters and setters,
}
This is the the embedded id mapping,
#Embeddable
public class EmployeeId implements Serializable{
public EmployeeId(){}
public EmployeeId(Integer employeeId,String empCountry){
this.employeeId = employeeId;
this.empCountry = empCountry;
}
#Column(name="EMP_ID")
private Integer employeeId;
#Column(name="COUNTRY")
private String empCountry;
// only getters and implementation of hashcode and equals method
}
And this is what I am trying to run in my main method:
EmployeeOne manager = new EmployeeOne("Yousuf Ahmadinejad", "IRAN");
em.persist(manager);
But here i am getting an exception i.e.
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: Attempt to modify an identity column 'EMP_ID'.
It's not like i didn't understood the exception,
but why this exception occured in the first place? I already annotated it with #GenerateValue for Empid and I am not setting the empId manually. Does this exception occur because I have combined primary key as empId and country, and than the empId is autogenerated using Identity, hence its giving an exception ?
Can you please tell me whats going wrong
One more thing i want to add here is, if i removed #Column and #Embeddeble annotation for EmployeeId.java, and than run, i get an following exception,
Caused by: org.hibernate.PropertyAccessException: could not set a field value by reflection setter of com.entities.derived.EmployeeId.employeeId
So just trying to find the solution to persist employee keeping the autogenerated Id as it is
First Hibernate does not generate id's for composite keys, so you should change EmployeeOne to:
#Id
//#GeneratedValue(strategy=GenerationType.IDENTITY) remove this line
#Column(name="EMP_ID")
private Integer employeeId;
Second that's not how you should implement EmployeeId composite key class. See: https://stackoverflow.com/a/3588400/1981720
Third, the exception is thrown by the database, not Hibernate. Check if you're getting the same exception with another database.

org.hibernate.ObjectNotFoundException issue with using list()

The following query throws the exception:
Query query = session.createQuery("from Associate as a order by a.username asc");
associates = query.list();
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [ca.mypkg.model.Associate#0]
If I create an entry in the database with id of 0 it works just fine. I don't really get it because I'm just trying to load all the entries in the db not just a specific one.
Similar questions I've found have been concerned with trying to load an object with a given ID I'm doing no such thing.
Associate class:
#Table(name = "user")
#XmlRootElement(name = "associate")
public class Associate implements Serializable {
private String username;
private String password;
private String firstName;
private String lastName;
private String userType;
private int id;
private String email;
private String isActive;
private Department dept;
private String lastUpdated;
private String associate_type;
// ...
#Id
#GeneratedValue
public int getId() {
return id;
}
#OneToOne
#JoinColumn(name = "dept")
public Department getDept() {
return dept;
}
From my experience this type of error message usually means it does not find joined entity by mentioned id, and not the entity requested in the query (Associate, in your case).
My guess is that Associate class contains a join entity which has primitive type primary key.

JPA , many-to-many relation, deleting all previous relations and entering the new relations

here i am trying out many-to-many relationship in JPA, I'v created tables "tblcourse" and "tblStudent", a student can register to many courses,
create table tblcourse(
id integer primary key,
name varchar(100),
duration integer
);
create table tblcourseStudent(
studentid integer references tblstudent(studentId),
courseId integer references tblcourse(id),
constraint pk_composit_cs primary key(studentid,courseId)
)
Create table tblStudent(
studentId integer primary key,
……..
….
);
The JPA representation of the above relation is as follows,
this is the code for StudentEntity.java,
#Entity
#Table(name="TBLSTUDENT")
public class StudentEntity implements Serializable{
private static final long serialVersionUID = 100034222342L;
#Id
#Column(name="STUDENTID")
private Integer studentId;
#Column(name="STUDENTNAME")
private String studentName;
#Column(name="CONTACTNO")
private String contactNumber;
#Embedded
private StudentAddress address;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="DEPTID")
private DeptEntity deptEntity;
#ManyToMany(fetch=FetchType.LAZY)
#JoinTable(name="tblcourseStudent",
joinColumns=#JoinColumn(name="studentid"),
inverseJoinColumns=#JoinColumn(name="courseId"))
private List<CourseEntity> courseList;
....
.....
.....
}
this the code for CourseEntity.java,
#Entity
#Table(name="TBLCOURSE")
public class CourseEntity implements Serializable{
public CourseEntity(){
}
public CourseEntity(Integer courseId,String courseName,Integer courseDuration){
this.courseId = courseId;
this.courseName = courseName;
this.courseDuration = courseDuration;
}
/**
*
*/
private static final long serialVersionUID = -2192479237310864341L;
#Id
#Column(name="ID")
private Integer courseId;
#Column(name="NAME")
private String courseName;
#Column(name="DURATION")
private Integer courseDuration;
#ManyToMany(fetch=FetchType.LAZY)
#JoinTable(name="tblcourseStudent",
joinColumns=#JoinColumn(name="courseId"),
inverseJoinColumns=#JoinColumn(name="studentid"))
private List<StudentEntity> studentList;
.........
}
Now, when i try to insert courses throught StudentEntity.java,
the SQL queries fired at backend are
delete
from
tblcourseStudent
where
studentid=?
insert
into
tblcourseStudent
(studentid, courseId)
values
(?, ?)
insert
into
tblcourseStudent
(studentid, courseId)
values
(?, ?)
And, when i try to insert students throught CourseEntity.java,
the SQL queries fired are as follows,
delete
from
tblcourseStudent
where
courseId=?
insert
into
tblcourseStudent
(courseId, studentid)
values
(?, ?)
in both of my case, the records are deleted and than the new mapping is inserted.
So if i am inserting Courses for a student, first all the previouse courses for the student will be deleted from the third table, and the new courses will be entered,
So, my question is, if i don't want to delete old courses and add the new courses for the student how can i achieve, i.e i want to retain the old relationship,
Weather i have to achieve this programatically,
or i have change the annotation,
Waiting for the reply
This the code written in StudentServiceBean.java and the method "mapStudentToCourses" gets called when we map a single student to multiple Courses
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER)
public class StudentServiceBean implements StudentService{
#PersistenceContext(unitName="forPractise")
private EntityManager entityMgr;
#Resource
private SessionContext sessionContext;
#EJB
private DeptService deptService;
..........
......
...
#Override
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void mapStudentToCourses(Integer studentId,String courseIdList) throws Exception{
List<CourseEntity> courseList = null;
StudentEntity studentEntity = null;
TypedQuery<CourseEntity> courseQuery = null;
String query = "select c from CourseEntity c where c.courseId in ("+courseIdList+")";
try{
courseQuery = entityMgr.createQuery(query,CourseEntity.class);
courseList = courseQuery.getResultList();
studentEntity = entityMgr.find(StudentEntity.class, studentId);
studentEntity.setCourseList(courseList);
entityMgr.merge(studentEntity);
}catch(Exception e){
sessionContext.setRollbackOnly();
throw e;
}
}
This is the code when one Course is mapped to multiple students, its CourseServiceBean.java
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER)
public class CourseServiceBean implements CourseService{
#PersistenceContext(name="forPractise")
private EntityManager em;
#Resource
private SessionContext sessionCtx;
private Map<Integer, String> durationCode = null;
#EJB
private StudentService studentService;
........
......
...
#Override
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void mapCourseToStudents(Integer courseId,String studentIdList) throws Exception{
List<StudentEntity> studentEntityList = null;
TypedQuery<StudentEntity> studentQuery = null;
String query = "select s from StudentEntity s where s.studentId IN ("+studentIdList+")";
CourseEntity courseEntity = null;
try{
studentQuery = em.createQuery(query, StudentEntity.class);
studentEntityList = studentQuery.getResultList();
courseEntity = em.find(CourseEntity.class,courseId);
courseEntity.setStudentList(studentEntityList);
em.merge(courseEntity);
}catch(Exception e){
sessionCtx.setRollbackOnly();
throw e;
}
}
}
this my persistence.xml file,
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="forPractise" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/app</jta-data-source>
<class>com.entity.StudentEntity</class>
<class>com.entity.DeptEntity</class>
<class>com.entity.CourseEntity</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.DerbyDialect" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
waiting for the reply....
I might be wrong but I think this is normal that when you're making your insertions, Hibernate firsts deletes all the records from the associated table.
That's the reason : when working with x-to-many association (basically, associations that are mapped through a Collection), Hibernate's persistence context will perfom the dirty checkings based on the Collection's identifier.
Let's take the mapCourseToStudents() method from your CourseServiceBean class :
...
studentQuery = em.createQuery(query, StudentEntity.class);
studentEntityList = studentQuery.getResultList();
courseEntity = em.find(CourseEntity.class,courseId);
courseEntity.setStudentList(studentEntityList); // replacing the previous Collection by the one you retrieved by querying the DB !!!
em.merge(courseEntity);
...
If you really want to avoid Hibernate executing the delete statement first, you should add/remove items to the Collection instead of assigning a new Collection and configure in the mapping data the operations that you want to cascade.
Add new courses to the existing list:
Collection<Student> moreStudents = ...
course = em.find(CourseEntity.class,courseId);
course.getStudentList().addAll(moreStudents);
I didn't overwrite the whole relationship list, and instead I add new entity to the original list. But, Hibernate still delete all my previous relationships.
According to this article:
https://vladmihalcea.com/the-best-way-to-use-the-manytomany-annotation-with-jpa-and-hibernate/
This seems to be the current behaviour of Hibernate, and if we don't want that, we have to first correctly implement our entity's hashCode() and equals() methods, and use Set to model the ManyToMany relationship.
I faced similar challange .
I am providing the similar kind of scenario hope it may be helpful to some people.
i am taking Product and Order to explain the issue and using uni-directional manyToMany mapping
#Entity
#Table(name="Product")
public class Product {
#Id
#Column(name = "id")
private Long id;
#Column(name = "product")
private String product;
#ManyToMany(cascade = CascadeType.PERSIST,fetch= FetchType.EAGER)
#JoinTable(name = "product_order_mapping", joinColumns = { #JoinColumn(name = "product_id") }, inverseJoinColumns = {
#JoinColumn(name = "order_id") })
#JsonProperty("orders")
private Set<Order> orders =new HashSet<Order>();
//setters and getters
}
#Table(name="Order")
public class Order {
#Id
#Column(name = "id")
private Long id;
#Column(name = "order")
private String order;
//setters and getters
//equals and hashcode
}
Implementation Logic
productList and OrderFunctions are some list with list of products and orders details
for (Product pEntity : productList) {
OrderFunctions = this.getAllLineFunctions(product.getId(), cmEntity.getId())
.getBody();
Set<Order> oList = new HashSet<>();
for (OrderFunction orderFn : OrderFunctions) {
Order orderEntity = new Order();
orderEntity.setId(lfs.getOrderFunction_id());
orderEntity.setStageGroup(lfs.getOrderFunctionOrder());
oList.add(sgEntity);
}
orderRepository.saveAll(oList);
pEntity.setOrders(oList);
productRepository.save(pEntity);
}
}
To understand duplicacy mapping let us take an example
Suppose Product is under a Company
Now a Company has multiple Product
And Product has Multiple Order
Now Let us take a practical example for storing data in tables
set1-->
company with id c1 has Product with id p1,p2,p3
product p1 has order o1,o2,o3
product p2 has order o1,o2
product p3 has order o2,o3,o4
set2-->
company with id c2 has Product with id p1,p3
product p1 has order o1,o2
product p3 has order o2
Now when save set1 table product_order_mapping looks like
product_id order_id
---------------------
p1 o1
p1 o2
p1 o3
p2 o1
p2 o2
p3 o2
p3 o3
p3 o4
But when save set2 after set1 table product_order_mapping will look like
product_id order_id
---------------------
p1 o1
p1 o2
p2 o1
p2 o2
p3 o2
We can see the difference before saving set2 and after saving set2
mappings p1->o3 , p3->o3 and p3->o4 are lost
Reason for losing mapping
when we tried to store set2 we tried to override the duplicate entry like p1->o1,p1->o2 which are allready available
in the mapping table
So what happened is before adding these duplicate entry all mappings related to p1 are removed and then
p1->o1,p1->o2 got added
And so we lost some previous entry
Solution
Avoid adding duplicate entry from set2 like p1->o1,p1->o2 as they are already present in mapping table
for (Company cEntity : companyList) {
for (Product pEntity : productList) {
OrderFunctions = this.getAllOrderFunctions(cEntity.getId(), pEntity.getId());
Set<Order> oList = new HashSet<>();
for (OrderFunction orderFn : OrderFunctions) {
Order orderEntity = new Order();
orderEntity.setId(lfs.getOrderFunction_id());
orderEntity.setStageGroup(lfs.getOrderFunctionOrder());
oList.add(sgEntity);
}
Set<Order> collectSG = pEntity.getOrders().stream().filter(o->oList.contains(o)).collect(Collectors.toSet());
oList.removeAll(collectSG);
if(oList.size()>0) {
orderRepository.saveAll(oList);
pEntity.setOrders(oList);
productRepository.save(pEntity);
}
}
}
}

Categories

Resources