Can any one tell me why addEntity is not identifying separate column from alias? Both parent and child getting child value only. parents field is getting override by child filed.
If I keep pr.* first in select statement then it take parent value in both object.
My code is as below.
String sql="select ch.*,pr.* from users as ch inner join users as pr on ch.parent_id=pr.id";
Query
query=session.createSQLQuery(sql).addEntity("ch",UserDTO.class).addEntity("pr",UserDTO.class);
List<Object> result=query.list();
Object[] objects = (Object[])result.get(0);
UserDTO child=(UserDTO)objects[0];
UserDTO parent=(UserDTO)objects[1];
I have read this doc hibernate doc, it is suggesting to use as follow. But {} is not working in mysql select.
sess.createSQLQuery("SELECT {cat.*}, {mother.*} FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class)
Related
I'm new to ORM interface, and I'm trying to connect to my databases with Hibernate.
What I've figured out so far is:
With a serializable object, I can get a persistent object with
Person p = session.get(Person.class, serializable);
I can get all the objects by a list with
List people = session.createQuery("FROM Person").list();
What I need is to find a row that meets a certain condition, such as SELECT * FROM person WHERE name="Kim" AND age=30;
However, the above two aren't the ways to achieve this.
#Entity
#Table(name = "person")
public class Person {
#Id
private Integer id; // I can use this variable when using session.get(Person.class, serializable) , but I cannot know the id of my target row.
private String name;
private Integer age;
...
Should I iterate all the objects in people, and check whether all the member variables match what I want?
Is there any simple way to achieve this?
First and most importantly, never put user input in a query like this
SELECT * FROM person WHERE name="Kim" AND age=30;
You have to use Prepared Statements. Learn why from Bobby Tables.
Secondly, you should use the JPA interface EntityManager instead of Hibernate's Session as the second one anchors you to a specific implementation, rather than the wider standard.
With the EntityManager you get an object by id like this:
Person p = em.find(Person.class, id);
To get a list of People you can create a JPQL query like this:
TypedQuery<Person> query = em.createQuery("SELECT p FROM Person p WHERE p.name = :name AND p.age = :age", Person.class);
query.setParameter("name", "Kim"); // :param1 defines a parameter named "param1" in the query
query.setParameter("age", 30);
List<Person> results = query.getResultList();
You could also do this in one chain if you don't need to reuse the query with different parameters on a loop.
List<Person> results = em.createQuery(..., Person.class)
.setParameter("name", "Kim")
.setParameter("age", 30)
.getResultList();
The reason to put every call on a new row is in case an exception occurs it will give you the proper row to look for. If they're all in one row, then that's not very useful.
If your query is a SELECT, and it needs to return exactly one result every time, you can use getSingleResult() instead of getResultList(). If you do that and the query did return more than one result, it will throw a NonUniqueResultException. If the query did not return any results it will throw a NoResultException instead of returning null.
If your query is NOT a SELECT, then you have to use executeUpdate() to invoke it after setting the parameters.
There are many resources to get you started, but generally if its for a Hibernate version before 5.2 you should consider it outdated, and it will likely be more difficult.
Consider table A and B. Entire column in table A and only one column in table B need to be taken.Previously,a named query is created with which entire column values of table A are taken. Now i need to include one column of table B too.
In DAO class,below is code
X x=null
Query query = getEntityManager().createNamedQuery("AAA").setParameter("serialID",val);
x=(X) query.getSingleResult()
And in DTO class, AAA is part of class X which refers to table A. The query of AAA is below:
select X from A X where COLUMNVAL= serialID;
Now, table B also has COLUMNVAL too and i need to consider value of xyz column from that table.Consider xyz is mapped in class Q.
I tried the below query for which i got classcast exception as java.lang.object cannot be cast to A
select X, Q.xyz from A X, B Q where X.COLUMNVAL = Q.COLUMNVAL AND X.COLUMNVAL=serialID
Please favour how to proceed further. A has huge number of columns so ResultList is not preferred.
I tried the below query for which i got classcast exception as
java.lang.object cannot be cast to A
select X, Q.xyz from A X, B Q where X.COLUMNVAL = Q.COLUMNVAL AND
X.COLUMNVAL=serialID
if you wish to use that query, you'll non get objects of class A, but instead an Array of Object where the first element is an object of class and the second element is the value Q.xyz
so you should use
Object[] x;
x=(Object[]) query.getSingleResult()
Create a ResultDTO class, which extends entity class A thus having all fields from class A and have its own required field(xyz) of the entity class B. Now you can do this to get ResultDTO:
String queryString = "select a, b.xyz from A a join B b on(a.field = b.field)"
+ " where a.serialID = :serialID";
return (List<ResultDTO>) entityManager.createQuery(queryString)
.setParameter("serialID", serialId);
.unwrap(org.hibernate.query.Query.class)
.setResultTransformer(Transformers.aliasToBean(ResultDTO.class))
.getResultList();
Moreover, I would suggest you to read this post The best way to map a projection query to a DTO (Data Transfer Object) with JPA and Hibernate
I need to apply a SQL Query similiar to this.
SELECT
id as id,
c03 as c03,
c34 as c34
FROM
(SELECT
id,
c03,
c34
FROM
students
where
c34 in(
?, ?,?,?
)
order by
id desc) o
group by
c34;
And My Java code.
private final void retrieveStudents(){
final List result = currentSession()
.createSQLQuery("SELECT id as id,c03 as c03,c34 as c34 FROM (SELECT id,c34,c03 FROM students where c34 in(:filters) order by id desc) o group by c34;")
.setParameterList("filters",Arrays.asList(74,1812))
.list();
result.forEach(this::consumer1);
}
The query is Just OK. Returns a array of objets which i can iterate but i would like to return a Student object so i add.
.addEntity(Student.class)
But a error is throw which says
Column 'ACTIVE' not found.
Also i have try with .addScalar but the same thing happens.
.addScalar("id",org.hibernate.type.IntegerType.INSTANCE)
.addScalar("c03",org.hibernate.type.DateType.INSTANCE)
Which is a column from Student but is not on the projection my question how can i do that i just thought that applying in some way the alias would Hibernate populate the student entity.
All i want is a Student object with id,c03,c34 values populate.
What i am doing wrong is that possible?
For this use case, you don't want hibernate to treat Student as an entity, but as a DTO.
For this, do not use the addEntity method, but the setResultTransfomer :
final List result = currentSession()
.createSQLQuery("SELECT id as id,c03 as c03,c34 as c34 " +
"FROM (SELECT id,c34,c03 FROM students " +
"where c34 in(:filters) " +
"order by id desc) o group by c34")
.setParameterList("filters",Arrays.asList(74,1812))
.addScalar("id",org.hibernate.type.IntegerType.INSTANCE)
.addScalar("c03",org.hibernate.type.DateType.INSTANCE)
.addScalar("c34",org.hibernate.type.DateType.INSTANCE)
.setResultTransformer(Transformers.aliasToBean(Student.class))
.list();
This is working for non entity classes, provided the class has setters that match the names of the projected columns, and there is a no-arg constructor. I never tested this on an entity class.
If you do not have setters, but a constructor for those 3 fields, you can use :
// choose here the right constructor
java.lang.reflect.Constructor constructor = Student.class.getConstructors()...
// ...
.setResultTransformer(new AliasToBeanConstructorResultTransformer(constructor));
instead.
EDIT :
I would not use an entity as a DTO (but create a specific DTO for this use case) : what if one of your service think the entity was loaded the regular way (so is fully initialized), do some modifications on it (update a field for example), and save the entity ?
In the best case, a not-nullable field (on db side) will not have been initialized, and you'll get a ConstraintViolationException and trigger a rollback, keeping your data safe.
In the worst case, you'll be corrupting your data by setting to null all the fields of the entity but the three loaded ones.
I have two entity objects (A and B) that have a One-To-Many relationship. I am using JPA (Hibernate) to join these tables and query them for a specific result set, but the criteria I specify for the child table (B) are not applied when fetching my results. This is how I have defined the query:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<A> query = builder.createQuery(A.class);
Root<A> a = query.from(A.class);
Join<A, B> abJoined = a.join(A_.b);
query.distinct(true)
.where(builder.and(
builder.equal(a.get(A_.id), id),
builder.equal(a.get(A_.active), 1),
builder.equal(a.get(A_.location), 1011),
builder.equal(a.get(A_.status), "Pending"),
builder.equal(abJoined.get(B_.status), "Not Moved"),
builder.greaterThan(abJoined.get(B_.detailId), 0)
));
When I call entityManager.createQuery(query).getResultList(); I get one instance of entity 'A', but when I try to access 'B' through 'A' a.getB() the two criteria that I had specified for abJoined are not applied and I get all instances of 'B' that are joined to 'A'. Is there something more I need to do to get these criteria applied? If the criteria cannot be applied, is there a recommended method for removing the corresponding instances of 'B' from the result set?
I can provide more code or other details if necessary.
The query is used to select which A entities must be returned by the query. But the A entities will never be partial entities containing only a subset of their Bs. They will always be complete instances of A, reflecting the state of what A is in the database, and which B this A is related to.
BTW, even if this was possible (it's not, and explicitely forbidden by the JPA spec), your query would at least have to load the Bs as well, using a fetch join. Otherwise only the state of A is returned by the query, and the linked Bs are loaded lazily.
But in JPA, for a #OneToMany relationship, Query is fired first on the master, with all the Criteria and fetches just master records.
Later Individually the query is fired, BUT BUT BUT......the B table criteria will not contain all the conditions, but only one condition i.e. just the primary key VALUE of master records fetched in i.e just with one condition "b.A_id=[primarykeyfetched from master]"
SOLUTION is simple
Make an Independent class, in which we require the fields of #OneToMany relationship
STEP 1
public class AB {
private A a;
private B b; //#OneToMany with A in the Entity class definitions
public AB(){}
public AB(A a, B b){ ///Very Important to have this constructor
this.a=a;
this.b=b;
}
getter setter....
}
STEP 2
CriteriaQuery<AB> q = criteriaBuilder.createQuery(AB.class);
Root<A> fromA = criteriaQuery.from(A.class);
List<Predicate> predicates = new ArrayList<Predicate>();
Join<A,B> fromB = fromA.join("b", JoinType.INNER);/*"b",fieldName of B in entity Class A*/
criteriaQuery.select(criteriaBuilder.construct(AB.class,A,B));//looks AB's 2 param constr.
predicates.add(criteriaBuilder.equal(fromA.get("name"), filter.getName()));
predicates.add(criteriaBuilder.equal(fromB.get("salary"), filter.getSalary()));
..........
List<AB> ABDataList = typedQuery.getResultList();
for(AB eachABData :ABDataList){
....put in ur objects
obj.setXXX(eachABData.getA().getXXX());
obj.setYYY(eachABData.getB().getYYY());
}
This will give all the results applying all the criteria to master table A, and comparing the primary key of Table B instead of foreign key.
I have a scenario where I need to load all the child values in one case and some specific child values in other . I am using a single bean for both the cases and writing the queries using named query.
#namedqueries{
#namedQuery(name="query1") = "select parent from Parent parent",
#namedQuery(name="query2") = "select parent from Parent parent",
}
Class Parent {
#manytomany
#join mentioned my join condition here //
List<Child> child ;
}
Class Child
{
String A;
String B;
String C;
#manytomany(mappedby = "child")
List<parent> parent ;
}
Now in my query 2 I need to load only String A not String B and String C .
I tried using
"select parent.child .A from Parent parent" as Query 2
but getting the below error
"Attempting to navigate to relation field via multi-valued association and
jpql doesnt allow traversal through multi valued relationship. Try join instead"
So any suggestions on how to proceed on this ..
1) Should I have to create a new bean for each Query
2) Or Can we control the child object parameters in specific named queries
You are accessing a collection when you say select parent.child and you can't say select parent.child.A this is wrong according HQL standards. You need to do this using join as the error message is suggesting:
select c.A from parent as p join p.child as c
Then no, you don't have to create a new bean for each query.