How to retrieve a member object of a class using Hibernate? - java

Using following code I can successfully retrieve address fields of a user, to do that I need to define all its fields using Projection. Imagine address has 100 fields, in this case I have to define all of them.
I am wondering if I can return just address object of customer without defining all its fields in Proposition?
I know I can retrieve id of address and use that to retrieve its object, but I am wondering if there is ano other method rather than this or defining all its fields.
Hibernate
.....
Criteria cre = session.createCriteria(User.class, "user")
.createAlias("user.address", "addr");
cre.add(Restrictions.eq("user.id", ID));
ProjectionList pl = Projections.projectionList();
pl.add(Projections.property("addr.id").as("id"));
pl.add(Projections.property("addr.unit").as("unit"));
.......
cre.setProjection(pl);
Address address = (Address) cre.list().get(0);
I used the following as well but it runs into error (could not resolve property: addr of: com.myProject.User)
pl.add(Projections.property("addr").as("address"));
Java
#Entity
public Class User {
#Id
#GeneratedValue
private long id;
#OneToOne
private Address address;
...
}

Use JPQL/HQL:
select a from User u join u.address a where u.id = :userId
The Criteria API is more limited than JPQL, and can't select any other entity than the root entity. It shouldn't be used if the query doesn't have to be dynamically composed. Of course, if the association is bidirectional, you can simply use
select a from Address a where a.user.id = :userId
or its equivalent Criteria:
Criteria c = session.createCriteria(Address.class, "a");
c.createAlias("a.user", "u");
c.add(Restrictions.eq("u.id", userId));

If the result you pull in from a query will match the fields of a DAO you have defined. I would just type-cast the result from an hql or native SQL query.
Select *
From Address a
where a.id = :userid
Address addrObject = (Address) query.uniqueResult();

Do like this
Criteria criteria = session.createCriteria(User.class, "user")
.createAlias("user.address", "addr")
.add(Restrictions.eq("user.id", userId))
.setProjection(Projections.property("addr"));
Address address = (Address) criteria.list().get(0);

Couple of options:
use lazy="false" for Address object. If you have to use lazy=true for some reason, you can run this query in a separate session and override the lazy behavior in that session.
Use the database specific query to get a list of field names and then dynamically generate Projections by looping through the field names.
For example,
In mysql
SHOW COLUMNS FROM Address
In postgres
SELECT * FROM information_schema.columns
WHERE table_schema = your_schema
AND table_name = your_table
I hope this helps.

Related

JPA: Using criteriabuilder to find entities: Attribute name different from annotation?

I have a mysql database with employee information, each employee has a technical id as primary key. In MySQL to selcet row(s) matching criteria, i can just use to get the following statement (works)
SELECT * FROM database_test.employee WHERE fist_name='First1';
In Java i can also use this as a native statement to get what i want (works):
List<EmployeeEntity2> objects = m_em.createNativeQuery(
"SELECT * database_test.employee WHERE first_name='First1'",
EmployeeEntity2.class).getResultList();
However, i wanted to use the Criteriabuilder to get the same result and later generalize it for multiple columnName=columnEntry selections.
public List<EmployeeEntity2> testNoParameter() {
//Based on https://www.objectdb.com/java/jpa/query/criteria
CriteriaBuilder cb = m_em.getCriteriaBuilder();
CriteriaQuery<EmployeeEntity2> q = cb.createQuery(EmployeeEntity2.class);
Root<EmployeeEntity2> c = q.from(EmployeeEntity2.class);
ParameterExpression<String> p = cb.parameter(String.class);
//Works
//q.select(c).where(cb.equal(c.get("firstName"), p));
//Won't work
q.select(c).where(cb.equal(c.get("first_name"), p));
TypedQuery<EmployeeEntity2> query = m_em.createQuery(q);
query.setParameter(p, "First1");
List<EmployeeEntity2> results = query.getResultList();
return results;
}
Using "fist_name" - the column name annotation from the Entity - will yield the following java.lang.IllegalArgumentException with:
Unable to locate Attribute with the the given name [first_name] on this ManagedType [xx.xxx.database.EmployeeEntity2]
EmployeeEntity2 has "fist_name" annotation:
#Column(name = "first_name", nullable = false)
#Override
public String getFirstName() {
return super.getFirstName();
}
So "first_name" should exist, however (with some debugging) i found out that the attribute expected is for some reason "firstName" instead - which i have not defined/annotated - so where does it come from - and how can i use the column names actually defined in the database (column = "first_name")?
You should use property name of entity (not column name) to use it in criteria builder so instead of
q.select(c).where(cb.equal(c.get("first_name"), p));
use
q.select(c).where(cb.equal(c.get("firstName"), p));
CriteriaBuilder is RDBMS schema agnostic, so you use your model (entities), not schema (table names etc).
In JPA you dont normally use SQL but JPQL. Equivalent of your SQL in JPQL would be something like
"SELECT e FROM EmployeEntity2 e WHERE e.firstName='First1'"
Both CriteriaQuery tree and JPQL string are transformed down to the same query tree later on (can't remember the name), so they both must comply to the very same rules.

JPQL include elementCollection map in select statement

I have an #Entity class Company with several attributes, referencing a companies Table in my db. One of them represents a Map companyProperties where the companies table is extended by a company_properties table, and the properties are saved in key-value format.
#Entity
#Table(name = "companies")
public class Company extends AbstractEntity {
private static final String TABLE_NAME = "companies";
#Id
#GeneratedValue(generator = TABLE_NAME + SEQUENCE_SUFFIX)
#SequenceGenerator(name = TABLE_NAME + SEQUENCE_SUFFIX, sequenceName = TABLE_NAME + SEQUENCE_SUFFIX, allocationSize = SEQUENCE_ALLOCATION_SIZE)
private Long id;
//some attributes
#ElementCollection
#CollectionTable(name = "company_properties", joinColumns = #JoinColumn(name = "companyid"))
#MapKeyColumn(name = "propname")
#Column(name = "propvalue")
private Map<String, String> companyProperties;
//getters and setters
}
The entity manager is able to perform properly find clauses
Company company = entityManager.find(Company.class, companyId);
However, I am not able to perform JPQL Queries in this entity and retrieve the Map accordingly. Since the object is big, I just need to select some of the attributes in my entity class. I also do not want to filter by companyProperties but to retrieve all of them coming with the proper assigned companyid Foreign Key. What I have tried to do is the following:
TypedQuery<Company> query = entityManager.createQuery("SELECT c.id, c.name, c.companyProperties " +
"FROM Company as c where c.id = :id", Company.class);
query.setParameter("id", companyId);
Company result = query.getSingleResult();
The error I get is:
java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Problem compiling [SELECT c.id, c.name, c.companyProperties FROM Company as c where c.id = :id]. [21, 40] The state field path 'c.companyProperties' cannot be resolved to a collection type.
org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1616)
org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1636)
com.sun.enterprise.container.common.impl.EntityManagerWrapper.createQuery(EntityManagerWrapper.java:476)
Trying to do it with joins (the furthest point I got was with
Query query = entityManager.createQuery("SELECT c.id, c.name, p " +
"FROM Company c LEFT JOIN c.companyProperties p where c.id = :id");
does not give me either the correct results (it only returns the value of the property and not a list of them with key-value).
How can I define the right query to do this?
Your JPA syntax looks off to me. In your first query you were selecting individual fields in the Company entity. But this isn't how JPA works; when you query you get back the entire object, with which you can access any field you want. I propose the following code instead:
TypedQuery<Company> query = entityManager.createQuery("from Company as c where c.id = :id", Company.class);
query.setParameter("id", companyId);
Company result = query.getSingleResult();
Similarly, for the second join query I suggest the following code:
Query query = entityManager.createQuery("SELECT c" +
"FROM Company c LEFT JOIN c.companyProperties p WHERE c.id = :id");
query.setParameter("id", companyId);
List<Company> companies = query.getResultList();
The reason why only select a Company and not a property entity is that properties would appear as a collection inside the Company class. Assuming a one to many exists between companies and properties, you could access the propeties from each Company entity.
You are expecting to get a complete Company object when doing select only on particular fields, which is not possible. If you really want to save some memory (which in most cases would not be that much of a success) and select only some field, then you should expect a List<Object[]>:
List<Object[]> results = entityManager.createQuery("SELECT c.id, c.name, p " +
"FROM Company c LEFT JOIN c.companyProperties p where c.id = :id")
.setParameter("id", companyId)
.getResultList();
Here the results will contain a single array of the selected fields. You can use getSingleResult, but be aware that it will throw an exception if no results were found.

Relationships between JPQL, Java (and the Oracle DB)

I am confused about the above relationship.
My Oracle DB Tables:
xd_Users: user_id (pk), client_id (fk), name, doj, email, dol
xd_Managers: manager_id (pk), user_id (fk)
Corresponding Java Entities User and Manager relate to the above two tables respectively. Manager and User are separate, not related by inheritance, and have fields that correspond to the DB tables.
In my application, a Manager has to be a User.
I am writing the a(n as yet unfinished) method (in a class called PersistService) to retrieve a list of users who are managers.
public static ArrayList<User> getManagersForClient(Client client) {
Long clientId = client.getClientId();
EntityManager em = getEntityManager();
String sqlQuery = "SELECT u FROM XD_USERS u, XD_MANAGERS m WHERE u.CLIENT_ID = :clientId";
TypedQuery<User> query = em.createQuery(sqlQuery, User.class);
query = query.setParameter("clientId", clientId);
ArrayList<User> clientUsers = (ArrayList<User>) query.getResultList();
for (User user : clientUsers) {
}
return clientUsers;
}
The pseudo-sql query I constructed was (:client_id at the end is just the java variable, hence the pseudo-sql):
select * from users u join managers m on u.user_id = m.user_id where u.client_id = :client_id;
I am having trouble converting this to a valid JPQL query. I don't understand how to think about solving this. In particular, the relationship between the identification variable, the single-valued relationship field, the collection-valued relationship field and the persistent field is very confusing. And I am even more confused by this post. Please help!
If you have coded the related entities correctly then your sql select query is something like this in jpql (according to the structure of related tables you gave):
select u.userId,u.name,u.doj,u.email, u.dol, m.managerId
from User u
join u.manager m
where u.clientId = :client_id;

How to create mapping of composed object with Hibernate when using a complex sqlquery?

I am trying to use the below query with Hibernate's session.createSQLQuery.
The Entity object corresponding to user has an attribute called address.
The address object is created out of 5 fields from table 'user'.
If I do not use an SQLQuery it gets filled auto-magically.
However without the SQLQuery I can't get all the info I would get from the desired joins shown below.
The user entity object also attributes like accessPlan which I am filling up using
.addEntity("accessPlan", AccessPlan.class)
Query:
SELECT
user.*,
ap.*,
country.*,
auth.*,
GROUP_CONCAT(coup.code SEPARATOR ' ') coupons
FROM
user
INNER JOIN access_plan ap ON (user.access_plan = ap.id)
INNER JOIN country ON (user.country=country.code)
LEFT JOIN user_auth auth ON (user.id = auth.userid)
LEFT JOIN (
SELECT
trans.user_id,coupon.code
FROM
payments_transaction AS trans
INNER JOIN payments_coupon coupon ON (trans.payments_coupon_id=coupon.id)
) coup ON (user.id=coup.user_id)
GROUP BY user.id;
What can be the easiest way to fill up the composed address object while using the SQLQuery?
OR
Is there a way to avoid using SQLQuery for a query like this?
Please check below example from the section 'Returning multiple entities'
String sql = "SELECT ID as {c.id}, NAME as {c.name}, " +
"BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " +
"FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID";
List loggedCats = sess.createSQLQuery(sql)
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class).list()
In your case, cat = user, mother = address... somewhat like that.
I do not have anything to try out at the moment but I guess this will help.

How to retrieve Google Appengine Objects by id (Long value)?

i have declared an entity the following way:
public class MyEntity {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
#Persistent
private String text;
//getters and setters
}
Now I want to retrieve the objects using the id. I tried to manage it from the Google Appengine Data Viewer with "SELECT * FROM MyEntity Where id = 382005" or via a query in a servlet. I get no results returned. But i know for sure that the object with the id exists (i made a jsp which queries all objects in the db and displays them in the db).
So what is wrong in my query? Am I querying the wrong field? The Google Appengine Data Viewer names the field "ID/name" and it has the value "id=382005". Do I have to query with this names? I've tried but it didn't work out :(
You can use below since you are querying using the primary key:
MyEntity yourEntity = entityManager.find(MyEntity.class, yourId);
Note, this should work as well, but it's easier to use find() if you are searching based on the primary key:
Query query = entityManager.createQuery(
"SELECT m FROM MyEntity m WHERE id = :id");
query.setParameter("id", yourId);
MyEntity yourEntity = (MyEntity) query.getSingleResult();

Categories

Resources