avoiding distinct keyword in batch query - java

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();

Related

JPA createQuery where condition does not work

I am trying to use JPA to fetch records from database. However I am able to insert records indatabse and even get all the records using createQuery method of class EntityManager.
But in below case I am not getting why the condition in where clause is not working.
Please help me figure it out.
POJO class :
#Entity
#Table(name = "frameworks_filter")
public class FilteredFrameworksDbStructure {
#Id
#Column(name="id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "regular_name")
private String regularName;
#Column(name = "component_name")
private String componentName;
#Column(name = "component_owner")
private String componentOwner;
#Column(name = "frameworks")
private String frameworks;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRegularName() {
return regularName;
}
public void setRegularName(String regularName) {
this.regularName = regularName;
}
public String getComponentName() {
return componentName;
}
public void setComponentName(String componentName) {
this.componentName = componentName;
}
public String getComponentOwner() {
return componentOwner;
}
public void setComponentOwner(String componentOwner) {
this.componentOwner = componentOwner;
}
public String getFrameworks() {
return frameworks;
}
public void setFrameworks(String frameworks) {
this.frameworks = frameworks;
}
}
DAO class method:
public List<FilteredFrameworksDbStructure> getFilteredFrameworks(String regularName) {
EntityManager entityManager = entityManagerFactory.createEntityManager();
List<FilteredFrameworksDbStructure> filteredFrameworksDbStructureList = entityManager
.createQuery("from FilteredFrameworksDbStructure F where F.regularName = :regular", FilteredFrameworksDbStructure.class)
.setParameter("regular", regularName)
.getResultList();
return filteredFrameworksDbStructureList;
}
Issue : Condition in where clause does not work. It simply fetch all the records irrespective of the regularName provided.
Regards,
Parag Vinchurkar
Why don't you use the JpaRepository or CrudRepository to fetch your results? Check out this tutorial here and here on how to use them.
And you can use your where clause. Please see below the example repository you can use to obtain the same results as the entityManager
public interface FilteredFrameworksDbStructureRepo extends JpaRepository<FilteredFrameworksDbStructure , Integer>{
List<FilteredFrameworksDbStructure> findAllByRegularName(String regularName)
}
Please note that you will have to change your id member variable from int to Integer

Insert with HQL query

I want to insert a new record to MySQL.
database: enter image description here
Entity class:`package entites;
// Generated Aug 11, 2017 1:32:55 PM by Hibernate Tools 4.3.1
import java.util.Date;
/**
* UserMonhoc generated by hbm2java
*/
public class UserMonhoc implements java.io.Serializable {
private UserMonhocId id;
private Monhoc monhoc;
private User user;
private Date thoigianDk;
public UserMonhoc() {
}
public UserMonhoc(UserMonhocId id, Monhoc monhoc, User user, Date thoigianDk) {
this.id = id;
this.monhoc = monhoc;
this.user = user;
this.thoigianDk = thoigianDk;
}
public UserMonhocId getId() {
return this.id;
}
public void setId(UserMonhocId id) {
this.id = id;
}
public Monhoc getMonhoc() {
return this.monhoc;
}
public void setMonhoc(Monhoc monhoc) {
this.monhoc = monhoc;
}
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user = user;
}
public Date getThoigianDk() {
return this.thoigianDk;
}
public void setThoigianDk(Date thoigianDk) {
this.thoigianDk = thoigianDk;
}
}
`
Code:
public boolean InsertStudent(String idUS, String idMH) {
try {
sf.getCurrentSession().beginTransaction();
UserMonhoc umh = new UserMonhoc();
String hql2 = "INSERT INTO User_Monhoc(user_id,mh_id) VALUES('" + idUS + "','" + idMH + "')";
System.out.println(hql2);
Query query = sf.getCurrentSession().createSQLQuery(hql2);
query.executeUpdate();
return true;
} catch (Exception e) {
return false;
}
}
it don't work.
Thank you.
Insert with HQL query
You don't use a HQL query.
This invocation :
Session.createSQLQuery(...)
creates a native SQL Query.
Anyway, you could not use INSERT in this way in HQL.
According to Hibernate documentation :
Only the INSERT INTO ... SELECT ... form is supported. You cannot
specify explicit values to insert.
But using a HQL query will not solve your problem as you want to specify values to insert.
To persist an entity, the most natural way of doing in Hibernate is invoking theSession.persist() method :
UserMonhoc umh = new UserMonhoc();
... // set fields of the object
sf.getCurrentSession().persist(umh);
But to do it, UserMonhoc of course has to be declared as an entity.
I specify it as I don't see any Hibernate annotation in your actual entity. These are maybe declared in the xml configuration...

Is it possible to use raw SQL within a Spring Repository

I need to use raw SQL within a Spring Data Repository, is this possible? Everything I see around #Query is always entity based.
The #Query annotation allows to execute native queries by setting the nativeQuery flag to true.
Quote from Spring Data JPA reference docs.
Also, see this section on how to do it with a named native query.
YES, You can do this in bellow ways:
1. By CrudRepository (Projection)
Spring Data Repositories usually return the domain model when using query methods. However, sometimes, you may need to alter the view of that model for various reasons.
Suppose your entity is like this :
import javax.persistence.*;
import java.math.BigDecimal;
#Entity
#Table(name = "USER_INFO_TEST")
public class UserInfoTest {
private int id;
private String name;
private String rollNo;
public UserInfoTest() {
}
public UserInfoTest(int id, String name) {
this.id = id;
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", nullable = false, precision = 0)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Basic
#Column(name = "name", nullable = true)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Basic
#Column(name = "roll_no", nullable = true)
public String getRollNo() {
return rollNo;
}
public void setRollNo(String rollNo) {
this.rollNo = rollNo;
}
}
Now your Projection class is like below. It can those fields that you needed.
public interface IUserProjection {
int getId();
String getName();
String getRollNo();
}
And Your Data Access Object(Dao) is like bellow :
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import java.util.ArrayList;
public interface UserInfoTestDao extends CrudRepository<UserInfoTest,Integer> {
#Query(value = "select id,name,roll_no from USER_INFO_TEST where rollNo = ?1", nativeQuery = true)
ArrayList<IUserProjection> findUserUsingRollNo(String rollNo);
}
Now ArrayList<IUserProjection> findUserUsingRollNo(String rollNo) will give you the list of user.
2. Using EntityManager
Suppose your query is "select id,name from users where roll_no = 1001".
Here query will return an object with id and name column. Your Response class is like bellow:
Your Response class is like this:
public class UserObject{
int id;
String name;
String rollNo;
public UserObject(Object[] columns) {
this.id = (columns[0] != null)?((BigDecimal)columns[0]).intValue():0;
this.name = (String) columns[1];
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRollNo() {
return rollNo;
}
public void setRollNo(String rollNo) {
this.rollNo = rollNo;
}
}
here UserObject constructor will get an Object Array and set data with the object.
public UserObject(Object[] columns) {
this.id = (columns[0] != null)?((BigDecimal)columns[0]).intValue():0;
this.name = (String) columns[1];
}
Your query executing function is like bellow :
public UserObject getUserByRoll(EntityManager entityManager,String rollNo) {
String queryStr = "select id,name from users where roll_no = ?1";
try {
Query query = entityManager.createNativeQuery(queryStr);
query.setParameter(1, rollNo);
return new UserObject((Object[]) query.getSingleResult());
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
Here you have to import bellow packages:
import javax.persistence.Query;
import javax.persistence.EntityManager;
Now your main class, you have to call this function. First get EntityManager and call this getUserByRoll(EntityManager entityManager,String rollNo) function. The calling procedure is given below:
Here is the Imports
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
get EntityManager from this way:
#PersistenceContext
private EntityManager entityManager;
UserObject userObject = getUserByRoll(entityManager,"1001");
Now you have data in this userObject.
Note:
query.getSingleResult() return a object array. You have to maintain the column position and data type with the query column position.
select id,name from users where roll_no = 1001
query return a array and it's [0] --> id and [1] -> name.
More info visit this thread and this Thread
Thanks :)
It is possible to use raw query within a Spring Repository.
#Query(value = "SELECT A.IS_MUTUAL_AID FROM planex AS A
INNER JOIN planex_rel AS B ON A.PLANEX_ID=B.PLANEX_ID
WHERE B.GOOD_ID = :goodId",nativeQuery = true)
Boolean mutualAidFlag(#Param("goodId")Integer goodId);
we can use createNativeQuery("Here Native SQL Query ");
for Example :
Query q = em.createNativeQuery("SELECT a.firstname, a.lastname FROM Author a");
List<Object[]> authors = q.getResultList();
This is how you can use in simple form
#RestController
public class PlaceAPIController {
#Autowired
private EntityManager entityManager;
#RequestMapping(value = "/api/places", method = RequestMethod.GET)
public List<Place> getPlaces() {
List<Place> results = entityManager.createNativeQuery("SELECT * FROM places p limit 10").getResultList();
return results;
}
}
It is also possible to use Spring Data JDBC, which is a fully supported Spring project built on top of Spring Data Commons to access to databases with raw SQL, without using JPA.
It is less powerful than Spring Data JPA, but if you want lightweight solution for simple projects without using a an ORM like Hibernate, that a solution worth to try.

How to handle join query in Hibernate and Spring with annotations?

I am developing application using Spring and Hibernate with MySQL. I am new to Hibernate and did basic tasks...
Now I need to apply joins in select query to get data from multiple table using annotations. I have searched for it but still I didn't get any idea...
Here my database tables and bean classes :
Table 1: 'employee_info' ( id, empid, empname, doj and jobtitle )
Table 2: 'employee_login' ( username, password, status and empid )
And my bean classes are:
EmployeeInfoForm.java
#Entity()
#Table(name = "employee_info")
public class EmployeeInfoForm {
#Id
#GeneratedValue
#Column(name = "id", unique = true, nullable = true)
private int id;
#Column(name = "empId")
private int empId;
#Column(name = "empname")
private String empName;
#Column(name = "doj")
private Date empDoj;
#Column(name = "jobtitle")
private String empJobTitle;
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public void setEmpDoj(Date empDoj) {
this.empDoj = empDoj;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public Date getEmpDoj() {
return empDoj;
}
public void setEmp_Doj(Date empDoj) {
this.empDoj = empDoj;
}
public String getEmpJobTitle() {
return empJobTitle;
}
public void setEmpJobTitle(String empJobTitle) {
this.empJobTitle = empJobTitle;
}
}
EmployeeLoginForm.java
#Entity()
#Table(name = "employee_login")
public class EmployeeLoginForm {
#Id
#Column(name = "username")
private String empUserName;
#Column(name = "password")
private String empPassword;
#Column(name = "status")
private String empStatus;
#Column(name = "empid")
private int empId;
public String getEmpUserName() {
return empUserName;
}
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public void setEmpUserName(String empUserName) {
this.empUserName = empUserName;
}
public String getEmpPassword() {
return empPassword;
}
public void setEmpPassword(String empPassword) {
this.empPassword = empPassword;
}
public String getEmpStatus() {
return empStatus;
}
public void setEmpStatus(String empStatus) {
this.empStatus = empStatus;
}
}
Requirement:
I want to select fields empid, empname, jobtitle from employee_info and field status from employee_login table when the empid matches on both table...
Please help me to complete my work...
Any suggestions and guidance are appreciated...
There is an association between EmployeeInfoForm and EmployeeLoginForm that I am not seeing in your code. Maybe there is an Employee class there? If that is the case then you need to add that. So let us assume that each employee has many forms. Then you will code the Employee side of the relationship like this:
public class Employee{
#OneToMany(cascade = CascadeType.ALL, mappedBy = "employee")
private Set<EmployeeLoginForm> loginForms = new HashSet<EmployeeLoginForm>();
...
}
And the Many side of the relationship in the EmployeeLoginForm class:
#ManyToOne
Employee employee;
This will create the table structure such that:
emploee = (id, etc ...)
employeelogin = (id, employee, ....)
Now, any time you need a list of the Logins of an Employee you get it from the Employee object without needing a Query.
Set<EmployeeLoginForm> logins = e.getLoginForms(); //where e is an employee object.
If you did want to query you can do
select o from EmployeeLoginForm o join o.employee
But that is unnecessary in this case.
You are thinking in database / pure SQL terms when you talk about performing joins with select statements. The power (and danger) of Hibernate is that it abstracts this away from you and lets you think in Object terms. What you need is a relationship between the 2 objects and then let Hibernate handle this relationship.
I recommend you spend some time reading this:
http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/associations.html
to get a better understanding of how Hibernate can help.
You can do the following using the Hibernate criteria projection:
public List extractEmployeeAttributes() {
log.debug("extractEmployeeAttributes");
try {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
Criteria c1 = session.createCriteria(employee_info.class,emp_info);
Criteria c2 = session.createCriteria(employee_login.class,emp_log);
c1.setProjection(Projections.projectionList()
.add(Projections.property("empid"))
.add(Projections.property("empname"))
.add(Projections.property("jobtitle"))
.add(Projections.property("employee_info "))
.add(Restrictions.and(Property.eqName(emp_info.empId,emp_log.empId))
return c1.list();
} catch (RuntimeException re) {
log.error("extractEmployeeAttributes failed", re);
throw re;
}
}

com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'productId3' in 'where clause'

I get an error com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'productId3' in 'where clause' when trying to run this query:
public Vendor getVendorId(ProductInfo productInfo) {
return (Vendor) this.sessionFactory
.getCurrentSession()
.createQuery(
"select vendorId from Product where productId3 = :id")
.setParameter("id", productInfo).uniqueResult();
}
The class Product looks like this:
#Entity
#Table(name="PRODUCT")
public class Product{
#Id
#GeneratedValue
#Column(name="PRODUCT_ID2")
private Integer productId2;
public void setProductId2(Integer productId2){
this.productId2 = productId2;
}
public Integer getProductId2(){
return productId2;
}
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="PRODUCT_ID3")
private ProductInfo productId3;
public void setProductId3(ProductInfo productId3){
this.productId3 = productId3;
}
public ProductInfo getProductId3(){
return productId3;
}
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="VENDOR_ID")
private Vendor vendorId;
public void setVendorId(Vendor vendorId){
this.vendorId = vendorId;
}
public Vendor getVendorId(){
return vendorId;
}
#OneToMany(mappedBy="productId2")
private Set<ProductRevision> productRevisions;
public void setProductRevisions(Set<ProductRevision> productRevisions){
this.productRevisions = productRevisions;
}
public Set<ProductRevision> getProductRevisions(){
return productRevisions;
}
}
Why do I get this error? I don't understand why it can't find the column as the name is exactly the same. Thanks for your help!
/D
Use this query:
select p.vendorId from Product p where p.productId3 = :id
Forgive the fussy response, but one thing that might HQL clearer for you is to rename your fields on the Product class.
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="PRODUCT_ID3")
private ProductInfo productInfo;
public void setProductInfo(ProductInfo productInfo){
this.productInfo = productInfo;
}
And similarly for the vendorId field. Then your HQL reads much better. It is clear that you are dealing with entities and not sql columns.
public Vendor getVendorId(ProductInfo productInfo) {
return (Vendor) this.sessionFactory
.getCurrentSession()
.createQuery(
"select p.vendor from Product where productInfo = :productInfo")
.setParameter("productInfo", productInfo).uniqueResult();
}

Categories

Resources