I have College and Student entities having OneToMany relationship:
#Entity
public class College {
#Id
#GeneratedValue
private int collegeId;
private String collegeName;
#OneToMany
private Collection<Student> students = new ArrayList<Student>();
public int getCollegeId() {
return collegeId;
}
public void setCollegeId(int collegeId) {
this.collegeId = collegeId;
}
public String getCollegeName() {
return collegeName;
}
public void setCollegeName(String collegeName) {
this.collegeName = collegeName;
}
public Collection<Student> getStudents() {
return students;
}
public void setStudents(Collection<Student> students) {
this.students = students;
}
}
#Entity
public class Student {
#Id
#GeneratedValue
private int studentId;
private String studentName;
public int getStudentId() {
return studentId;
}
public void setStudentId(int studentId) {
this.studentId = studentId;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
}
There is a foreign key to college in Student table.
If I want to fetch all students from a perticular college then in native SQL I can do tha with following query:
Select * from student where collegeId=1
Is it possible to achieve same with HQL by selecting from students entities and not from college entity.
My utility class:
public class ManyToOne {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("org.hibernate.examples");
EntityManager em = emf.createEntityManager();
College college1 = new College();
college1.setCollegeName("College1");
College college2 = new College();
college2.setCollegeName("College2");
Student student1 = new Student();
student1.setStudentName("std1");
college1.getStudents().add(student1);
Student student2 = new Student();
student2.setStudentName("std2");
college2.getStudents().add(student2);
Student student3 = new Student();
student3.setStudentName("std3");
college1.getStudents().add(student3);
Student student4 = new Student();
student4.setStudentName("std4");
college1.getStudents().add(student4);
em.getTransaction().begin();
em.persist(college1);
em.persist(college2);
em.persist(student1);
em.persist(student2);
em.persist(student3);
em.persist(student4);
em.getTransaction().commit();
em.close();
em = emf.createEntityManager();
em.getTransaction().begin();
String queryString = "select students from "+ College.class.getName()+" where collegeId = 1";
Query query = em.createQuery(queryString);
List<Student> students = query.getResultList();
for(int i=0;i<students.size();i++)
System.out.println(students.get(i).getStudentName());
em.getTransaction().commit();
em.close();
emf.close();
}
}
Exception stacktrace:
Exception in thread "main" java.lang.IllegalStateException: No data type for node: org.hibernate.hql.internal.ast.tree.IdentNode
\-[IDENT] IdentNode: 'students' {originalText=students}
at org.hibernate.hql.internal.ast.tree.SelectClause.initializeExplicitSelectClause(SelectClause.java:174)
at org.hibernate.hql.internal.ast.HqlSqlWalker.useSelectClause(HqlSqlWalker.java:924)
at org.hibernate.hql.internal.ast.HqlSqlWalker.processQuery(HqlSqlWalker.java:692)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:665)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:301)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:249)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:278)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:206)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:158)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:131)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:93)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:167)
at org.hibernate.internal.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:301)
at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:236)
at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:1800)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:328)
at utils.ManyToOne.main(ManyToOne.java:66)
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:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Try with this (just saw in your other question that mappings are ok)
String queryString = "select s from Student s where s.college.collegeId = 1";
EDIT
With regard to your comment, Hibernate has a "feature" where if it can't find a field with given name, it will just pass down to SQL whatever you give it. In your case it means that
String queryString = "from Student where collegeId=1";
since collegeId is not a field in Student, it will be passed into SQL as it is, resulting in the query you want. Of course, the downside is coupling of your code to underlying DB model, bypassing ORM mappings.
Related
I have two objects, Student and Book. Student has #OneToMany relationship with Book, and Book has only id, name, and publishYear. What i want is to return from Query is this -> Select s.name, b.name, b.publishYear from Student s inner join Book b on s.id = b.studentId. How to return s.name, b.name and b.publishYear?
entityManager.createQuery("select s.name, b.name, b.publishYear from Student s join s.books b");
This will return a List<Object[]>
If you want to return a list of DTOs you can use the constructor expression:
entityManager.createQuery("select new model.BookDTO(s.name, b.name, b.publishYear) from Student s join s.books b");
This will return a List<BookDTO>
The BookDTO must look like this:
package model;
public class BookDTO() {
private final String studentName;
private final String bookName;
private final String publishYear;
public BookDTO(String studentName, String bookName, String publishYear) {
this.studentName = studentName;
this.bookName = bookName;
this.publishYear = publishYear;
}
// getters
}
Read more about projection in the Hibernate documentation:
https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#hql-select-clause
guys i've tried different methods proposed here or in an other websites to fix this error, i've fixed it before but in another case, but this one seems a complicated one. , an entity named department which have a oneToMany relation with it self , so the departement can have one or many subDepartments , and a department should have one and only one parent department.
Department Entity :
#Entity
#Table(name = "DEPARTMENTS")
public class Department {
#Id
#Column(name = "dep_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private long depId;
private String depName;
#ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
#OnDelete(action = OnDeleteAction.CASCADE)
#JoinColumn(name = "supDep", referencedColumnName = "dep_Id")
#JsonIgnoreProperties(value ={"departments","users"} , allowSetters = true)
private Department supDep;
#OneToMany(mappedBy = "supDep", orphanRemoval = true, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIgnoreProperties(value ={"supDep","users"} , allowSetters = true)
private List<Department> departments = new ArrayList<>() ;
Constructors & getters &setters...
}
DepartmentRepository:
#Repository
public interface DepartmentRepository extends JpaRepository<Department,Long> {
Department findByDepName(String name);
}
DepartmentService Interface :
public interface DepartmentService {
Department add(Department department);
Department update(Department department, Long id);
void delete(long id);
List<Department> findAll();
Department findByName(String name);
Department findById(Long id);
User getChefDep(Long idDep);
}
DepartmentServiceImplement :
#Service(value = "departmentService")
public class DepartmentServiceImpl implements DepartmentService {
....
#Override
public Department add(Department department) {
Department newDep = new Department();
if(department.getDepId() != 0)
newDep.setDepId(department.getDepId());
newDep.setDepName(department.getDepName());
newDep.setChefDep(department.getChefDep());
newDep.setSupDep(department.getSupDep());
newDep.setDepartments(department.getDepartments());
newDep.setUsers(department.getUsers());
return departmentRepository.save(department);
}
...
}
DepartmentController ADD method :
#RestController
#CrossOrigin("*")
#RequestMapping("/department/")
public class DepartmentController {
...
#PostMapping("add")
public Department add(#RequestBody Department department) {
return departmentService.add(department);
}
...
}
anyway, when i add a new department with postman it works and the department is saved in DATABASE :
{
"depName": "marketing",
"supDep": null,
"departments": []
}
and when i add a new department with a supDep that doesn't exist in DATABASE it works too and the both entities are saved in DATABASE :
{
"depName": "Security",
"supDep":
{
"depName": "IT",
"supDep": null,
"departments": [],
"chefDep": 0,
}
}
but when i add a new department passing supDep a department that does exist :
{
"depName": "sub-marketing",
"supDep":
{
"depId": 1,
"depName": "marketing"
}
}
it throws this annoying error :
{
"timestamp": "2020-03-17T14:49:40.071+0000",
"status": 500,
"error": "Internal Server Error",
"message": "detached entity passed to persist: com.ats.remotetimemanager.Model.Department; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.ats.remotetimemanager.Model.Department",
"trace": "org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.ats.remotetimemanager.Model.Department; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.ats.remotetimemanager.Model.Department\r\n\tat org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:319)\r\n\tat org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:255)\r\n\tat org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:528)\r\n\tat org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)\r\n\tat org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)\r\n\tat org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153)\r\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)\r\n\tat org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:178)
The correct way to implement the method
#Override
public Department add(Department department) {
Department newDep;
if(department.getDepId() != 0) {
Optional<Department> optional = departmentRepository.findById(id);
if(optional.isPresent()) {
newDep = optional.get();
} else {
newDep = new Department();
}
} else {
newDep = new Department();
}
newDep.setDepName(department.getDepName());
newDep.setChefDep(department.getChefDep());
newDep.setSupDep(department.getSupDep());
newDep.setDepartments(department.getDepartments());
newDep.setUsers(department.getUsers());
return departmentRepository.save(department);
}
the getters & setters :
public long getDepId() {
return depId;
}
public void setDepId(long depId) {
this.depId = depId;
}
public String getDepName() {
return depName;
}
public void setDepName(String depName) {
this.depName = depName;
}
public Department getSupDep() {
return supDep;
}
public void setSupDep(Department supDep) {
this.supDep = supDep;
}
public List<Department> getDepartments() {
return departments;
}
public void setDepartments(List<Department> departments) {
this.departments = departments;
}
public long getChefDep() {
return chefDep;
}
public void setChefDep(long chefDep) {
this.chefDep = chefDep;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
#Override
public String toString() {
return "Department{" +
"depId=" + depId +
", depName='" + depName + '\'' +
", supDep=" + supDep +
", departments=" + departments +
", chefDep=" + chefDep +
'}';
}
}
I am able to successfully create and insert entries in a table via Hibernate, however for some reason my update method appears to not be working.
For my table, I chose to use Java annotations in the POJO file to create it.
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
/**
*
* #author
*/
#Entity
#Table(name="student") //name of DB table that will be created via Hibernate
public class Student {
#Id //Primary Key
#Column(name = "id") //map to column
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "marks")
private Integer marks;
public Student(Integer id, String name, Integer marks) {
this.id = id;
this.name = name;
this.marks = marks;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getMarks(){
return marks;
}
public void setMarks(Integer marks) {
this.marks = marks;
}
#Override
public String toString() {
return "Student: " + this.getId() + " | " + this.getName() + " | " + this.getMarks();
}
}
As aforementioned, the table is successfully created in a MySQL database. However, I am unable to update an objects Marks (grade) via my HQL query:
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/**
*
* #author
*/
public class HibernateModuleTen {
private static SessionFactory factory = new Configuration()
.configure("hibernate.cfg.xml")
.addAnnotatedClass(Student.class)
.buildSessionFactory();
public static void main(String[] args) {
Session newSession = factory.getCurrentSession();
try {
/*Create Student Objects
in Memory
*/
Student student1 = new Student(100, "Greg", 95);
Student student2 = new Student(101, "Mary", 91);
Student student3 = new Student(102, "Sidi", 90);
Student student4 = new Student(103, "Rokia", 92);
Student student5 = new Student(104, "Abdel", 88);
Student student6 = new Student(105, "Christine", 77);
Student student7 = new Student(106, "Hamma", 90);
Student student8 = new Student(107, "Ahmadu", 68);
Student student9 = new Student(108, "Halimatu", 96);
Student student10 = new Student(109, "Iziren", 99);
//Begin transaction
newSession.beginTransaction();
//Save all the students
newSession.save(student1);
newSession.save(student2);
newSession.save(student3);
newSession.save(student4);
newSession.save(student5);
newSession.save(student6);
newSession.save(student7);
newSession.save(student8);
newSession.save(student9);
newSession.save(student10);
newSession.getTransaction().commit();
//Update a Student Record
updateStudent(107, 34);
//Delete a record if marks are less than 35 and then update Database
deleteStudent();
//Print all records
newSession = factory.openSession();
newSession.beginTransaction();
Criteria newCriteria = newSession.createCriteria(Student.class);
List<Student> students = newSession.createQuery("from Student").list(); //.list is .getResultList in later versions of Hibernate
for (Student aStudent : students) {
System.out.println(aStudent.toString());
}
newSession.getTransaction().commit();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
factory.close();
}
}
public static void updateStudent(Integer id, Integer marks) throws HibernateException {
/*Update Transaction*/
Session newSession = factory.openSession();
newSession.beginTransaction();
Student studentToUpdate = (Student)newSession.get(Student.class, id); //Choose record 107 to update
//Update the marks of Student based on ID and marks
studentToUpdate.setMarks(marks);
newSession.update(studentToUpdate);
//Commit to the Transaction
newSession.getTransaction().commit();
newSession.close();
}
public static void deleteStudent() throws HibernateException {
Session newSession = factory.openSession();
newSession.beginTransaction();
newSession.createQuery("delete from student s where smarks < 35")
.executeUpdate(); //Used for updates and deletes
newSession.getTransaction().commit();
newSession.close();
}
}
The update method effectively takes one of the records in the table via the id column and updates the element in the marks column.
There was an issue with my delete method:
public static void deleteStudent() throws HibernateException {
Session newSession = factory.openSession();
newSession.beginTransaction();
newSession.createQuery("delete from student s where smarks < 35")
.executeUpdate(); //Used for updates and deletes
newSession.getTransaction().commit();
newSession.close();
}
If one looks closely at the query, the "delete from student" should be "delete from Student" with a capital s. Careless error.
You didn't post your errors here, but looks like your Student bean class doesn't have a default constructor which is being invoked while executing
Student studentToUpdate = (Student)newSession.get(Student.class, id);
You can try after adding a default constructor to Student class along with your custom constructor.
**This is student pojo class**
#Entity
#Table(name="student")
public class Student {
#Id
private int studentId;
private String StudentName;
#Embedded
private Adress add;
#Embedded
private Adress temp;
public void setTemp(Adress temp) {
this.temp = temp;
}
public void setAdd(Adress add) {
this.add = add;
}
public void setStudentId(int studentId) {
this.studentId = studentId;
}
public void setStudentName(String studentName) {
StudentName = studentName;
}}
** this is Address pojo class**
#Embeddable
public class Adress {
#Column(name="village" )
private String village;
#Column(name="pin")
private int pin;
public void setVillage(String village) {
this.village = village;
}
public void setPin(int pin) {
this.pin = pin;
}}
** Main class **
public class Test {
public static void main(String[] args) {
SessionFactory factory=Sessionfactory.getfactory();
Session session=factory.openSession();
Adress a=new Adress();
a.setPin(123);
a.setVillage("kanipakam");
Adress a2=new Adress();
a2.setVillage("kanchi");
a2.setPin(1234);
Student s=new Student();
s.setAdd(a);
s.setTemp(a2);
s.setStudentId(101);
s.setStudentName("uday");
Transaction tx=session.beginTransaction();
session.save(s);
tx.commit();
System.out.println("object saved");
session.close();
factory.close();
}}
** showing error**
while running this code its show below error please help me
Exception in thread "main" org.hibernate.MappingException: Repeated column in mapping for entity: com.sathya.pojo.Student column: pin (should be mapped with insert="false" update="false")
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:709)
at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:731)
at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:727)
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:753)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:506)
at org.hibernate.mapping.RootClass.validate(RootClass.java:270)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1358)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1849)
at com.sathya.singleton.Sessionfactory.getfactory(Sessionfactory.java:21)
at com.sathya.Test.Test.main(Test.java:16)
#Embedded annotation specifies that fields from the object are persisted in the same table as root entity. The names for the columns are taken from the #Embeddable configuration. So in your case
#Embedded
private Adress add;
#Embedded
private Adress temp;
Specifies that there are 2 objects of type Adress on your #Entity. The problem is that gived your current configuration add.village and temp.village both will be mapped to village column of student table.
You have 2 options to resolve the issue:
Override mappings for one (or both) #Embedded properties and specify different columns like described here
Use many-to-one or one-to-one relation to address and store address records in a separate database table.
Using Eclipselink 2.4.1, I'm trying to avoid the "distinct" keyword from appearing in batch queries. Documentation suggests that when using batch type EXISTS the distinct keyword isn't used, however in my experience it's used in all cases when retrieving a many-to-one relationship. For example, I have the following classes written against the SCOTT demo schema in oracle:
#Entity
public class Emp implements Serializable {
#Id private long empno;
private String ename;
#ManyToOne
#JoinColumn(name="DEPTNO")
private Dept dept;
public Emp() { }
public long getEmpno() { return this.empno; }
public void setEmpno(long empno) { this.empno = empno; }
public String getEname() { return this.ename; }
public void setEname(String ename) { this.ename = ename; }
public Dept getDept() { return this.dept; }
public void setDept(Dept dept) { this.dept = dept; }
}
#Entity
public class Dept implements Serializable {
#Id private long deptno;
private String dname;
public Dept() {}
public long getDeptno() { return this.deptno; }
public void setDeptno(long deptno) { this.deptno = deptno; }
public String getDname() { return this.dname; }
public void setDname(String dname) { this.dname = dname; }
}
I'm attempting to retrieve the employees and departments via batch fetch:
Query query = em.createQuery("select emp from Emp as emp ");
query.setHint(QueryHints.BATCH_TYPE, BatchFetchType.EXISTS);
query.setHint(QueryHints.BATCH, "emp.dept");
List<Emp> resultList = query.getResultList();
resultList.get(0).getDept();
The following SQL is generated:
[EL Fine]: sql: 2012-12-12 17:04:21.178--ServerSession(1034011695)--Connection(312759349)--SELECT EMPNO, ENAME, DEPTNO FROM SCOTT.EMP
[EL Fine]: sql: 2012-12-12 17:04:21.286--ServerSession(1034011695)--Connection(312759349)--SELECT distinct t0.DEPTNO, t0.DNAME FROM SCOTT.DEPT t0 WHERE EXISTS (SELECT t1.EMPNO FROM SCOTT.EMP t1 WHERE (t0.DEPTNO = t1.DEPTNO))
Is there something additional that needs to be done in addition to using EXISTS batch type to avoid the distinct keyword in batch queries?
Please log a bug. It should only be using this for JOIN batching.
You can set distinct to false on the query to avoid it. (there is no hint, you need to call dontUseDistinct() on the root ObjectLevelReadQuery).
((ObjectLevelReadQuery)((JpaQuery)query).getDatabaseQuery()).dontUseDistinct();