Hibernate Joins using Criteria - java

I am trying to use Hibernate to access persisted data for our rights management, but I am very new to it and struggling to get the data I need.
I have Users table, with an ID and name, a Groups table with an ID and name, and a User/Groups mapping which just consists of the group ids and user ids that are linked.
What I want to do is get all the names of the members of a given group, so the standard SQL query I want to execute is this:
SELECT
u.NAME
FROM
USERS u
JOIN
GROUP_USERS gu
ON
u.ID = gu.USER_ID
JOIN
GROUPS g
ON
gu.GROUP_ID = g.ID
WHERE
g.NAME = 'test'
But despite hours of looking and playing I cannot seem to get anywhere.
I want to use Criteria as they seem clearer, so my code is as follows:
#Override
public final List<String> getGroupMembers(final String groupName) {
#SuppressWarnings("unchecked")
List<User> groupUsers = getHibernateTemplate().execute(new HibernateCallback<List<User>>() {
#Override
public List<User> doInHibernate(Session session) throws HibernateException, SQLException {
Criteria criteria = session.createCriteria(User.class)
.setFetchMode("GroupUsers", FetchMode.JOIN)
.setFetchMode("Group", FetchMode.JOIN)
.add(Restrictions.eq("name", groupName));
return criteria.list();
}
});
List<String> groupUsernames = new ArrayList<String>();
for (User groupUser : groupUsers) {
groupUsernames.add(groupUser.getName());
}
return groupUsernames;
}
But when I test it the result set is empty, and according to the logs the executed query is this:
select this_.ID as M1_4_0_, this_.NAME as M2_4_0_ from USERS this_ where this_.NAME=?
I let Hibernate create the tables using hibernate.hbm2ddl.auto, but then removed it so that the tables are definitely as hibernate expects, but the data is not being cleaned.
Any help with the Criteria would be greatly appreciated, so thanks in advance!
Edit: I am using xml mapping files:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.company">
<class name="com.company.Group" table="GROUPS">
<id name="id" column="ID" type="int">
<generator class="identity"/>
</id>
<property name="name" column="NAME" type="java.lang.String" unique="true" not-null="true"/>
</class>
</hibernate-mapping>
and
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.company">
<class name="com.company.GroupUsers" table="GROUP_USERS">
<composite-id>
<key-many-to-one name="groupId" class = "Group" column="GROUP_ID" />
<key-many-to-one name="userId" class = "User" column="USER_ID" />
</composite-id>
</class>
</hibernate-mapping>
and
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.company">
<class name="com.company.User" table="USERS">
<id name="id" column="ID" type="int">
<generator class="identity"/>
</id>
<property name="name" column="NAME" type="java.lang.String" unique="true" not-null="true"/>
</class>
</hibernate-mapping>

You don't need to map the user-group table, just define your relation between User and Group as a many-to-many relation, take a look at this and this about how to map many-to-many relations.
In your case, it will look like:
<hibernate-mapping package="com.company">
<class name="com.company.User" table="USERS">
<id name="id" column="ID" type="int">
<generator class="identity"/>
</id>
<property name="name" column="NAME" type="java.lang.String" unique="true" not-null="true"/>
<many-to-many name="userGroups"
target-entity="com.company.Group">
<join-table name="YOUR_USER_GROUP_TABLE">
<join-column name="USER_ID" />
<inverse-join-column name="GROUP_ID" />
</join-table>
</many-to-many>
</class>
And to filter your users using the name field from the Group entity for example:
Criteria criteria = session.createCriteria(User.class);
criteria.createAlias("userGroups", "usrGrp",CriteriaSpecification.LEFT_JOIN);
criteria.add( Restrictions.eqProperty("usrGrp.name", "test") )

The way you map your object to tables does not benefit from the usage of Hibernate as ORM. Consider a more object-oriented model, eg:
class Group {
private Set<User> users;
// ...
}
class User {
private Set<Group> groups;
//..
}
So, you've got a classical many-to-many association. Here you can find an example of such mapping with Hibernate.

Related

Version filed in many-to-one relation causes object references an unsaved transient instance

i have a class like this :
public class User(){
private String name;
private Integer version ;
//getter & setter
}
and the hibernate mapping file is :
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="org.model.User" table="User">
<id name="id" column="Id" type="java.lang.Integer">
<generator class="sequence" >
<param name="sequence">SEQ_User</param>
</generator>
</id>
<version name="version" column="version" type="Integer" />
<property name="name" column="Name" type="string" not-null="true" />
</class>
</hibernate-mapping>
when i use as a many-to-one relation like this :
public class UserDetail(){
private String tel;
private User user;
// getter & setter
}
and this is UserDetail.hbm.xml file :
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="org.model.UserDetail" table="User_Detail">
<id name="id" column="Id" type="java.lang.Integer">
<generator class="sequence" >
<param name="sequence">SEQ_User_Detail</param>
</generator>
</id>
<property name="tel" column="tel" type="string" not-null="true" />
<many-to-one name="user" column="user_Id" entity-name="org.model.User" />
</class>
</hibernate-mapping>
when i want to save the UserDetail take this exception :
object references an unsaved transient instance - save the transient
instance before flushing
i save the UserDetail object in the way below :
#Override
public void add(UserDetail entity) {
Session session = getSession();
session.save(entity);
}
and i set User in the UserDetail like this :
entity.setUser(new User(1));
i just know the UserId and never load the user object.
after i debug if we put the version field Null in User i get the exception but when i set it with zero the problem is solved
Although you haven't added the code for UserDetails and the persistence logic, I suppose you have a #ManyToOne association to a User in UserDetails.
If you have a User and a UserDetails that you want to persist, you have to call persist() on both entities. Unless you have a parent-side association in User, you cannot use cascading since cascade makes sense only from parent to children but not vice versa.
This has nothing to do with concurrency or versioning as you initially suspected.
You said that:
I don't have any problem when i remove version field
That could be explained if you set an id to a transient User instance and you try to call persist():
User user = new User();
user.setId(1L);
user.setVersion(0);
session.persist(user);
Because you have a sequence identifier generator, it is Hibernate that should assign identifiers. Also, the version property should never be assigned manually. If you have a detached entity, you need to call merge instead of persist.
Update
According to your update and the new source code, I can't find any particular issue. However, you should know that the Session might have been opened before and maybe there are other entities that you added previously to creating the User entity. It's only during the flush that entity state transitions are propagated to the DB.

Hibernate Select wrong table name

Im trying to insert and select data in mysql database trough Hibernate and Insert is working fine for me but select somehow dont map the right table name and returns me no result.
Get and insert code:
SessionFactory sessFact = HibernateUtil.getSessionFactory();
Session session = sessFact.getCurrentSession();
session.beginTransaction();
session.save(obj);
session.getTransaction().commit();
try {
Session mysession = HibernateUtil.getSessionFactory().getCurrentSession();
mysession.beginTransaction();
weatherDataObject resultObjectHib = (weatherDataObject) mysession.get(weatherDataObject.class, 26);
mysession.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
}
sessFact.close();
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="some.pack">
<class name="weatherCoordinates" table="coordinates">
<id name="dataBaseId" column="coordinates_id">
<generator class="native" />
</id>
<property name="lat" type="string" column="coordinates_lat" />
<property name="lon" type="string" column="coordinates_lon" />
</class>
</hibernate-mapping>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="some.pack">
<class name="weatherDataObject" table="data_object">
<id name="id" column="data_object_id">
<generator class="native" />
</id>
<property name="name" type="string" column="data_object_name" />
<many-to-one name="coord" class="task.main.DataObjects.weatherCoordinates"
column="coordinates_id" unique="true" not-null="true" cascade="all" />
</class>
</hibernate-mapping>
When I see sql execution strings it is :
Hibernate: insert into coordinates (coordinates_lat, coordinates_lon) values (?, ?)
Hibernate: insert into data_object (data_object_name, coordinates_id) values (?, ?)
Hibernate: select weatherdat0_.data_object_id as data_obj1_1_0_, weatherdat0_.data_object_name as data_obj2_1_0_, weatherdat0_.coordinates_id as coordina3_1_0_ from data_object weatherdat0_ where weatherdat0_.data_object_id=?
The problem is weatherdat0 that somehow is wrong my table is called the way I mapped it in the file data_object don't know how and why it is changed anybody can help ?
The query is generated on correct table, as the select query is run on table from data_object
The weatherdat0_is just an alias for the table as mentioned in the from statement:
from data_object weatherdat0_
So it is picking correct table name only.
Now if the query is not returning any results means there are no records matching that id so I suggest you to run the query directly on database and see if it returns any records.

Discriminator formula involving referenced table and not main class table

I have hibernate XML where I'd like to retrieve a row as an instance of the subclass based on the results of a discriminator formula. Trouble is, the field to examine is on a referenced table, not on the table itself. My object is a merchant-user account, which is a combination of user account and merchant. The user_account table has a role field that may include several values, of which a few indicate a level of admin permissions. I'd like to do something like this, but this itself (trying to reference userAccount.role in the case statement) does not work:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.rc.model.mexp.MerchantUserAccount" table="merchant_user_account">
<id name="merchantUserAcctId" type="int">
<column name="merchantuseracctid" scale="10" precision="0" not-null="true" unique="true" sql-type="int unsigned"/>
<generator class="com.rc.model.jdbc.sequence.MexpIdentifierGenerator">
<param name="sequence">seq_merchantuseraccountid</param>
<param name="idDataType">int</param>
</generator>
</id>
<discriminator formula="case when userAccount.role in (2, 3, 4, 5) then '1' else '0' end" type="boolean"/>
<many-to-one name="userAccount" class="com.rc.model.user.security.UserAccount" column="userid"
unique="true" not-null="true" cascade="all"/>
<many-to-one name="merchantAccount" class="com.rc.model.mexp.MerchantAccount" column="merchantacctid"
not-null="true" cascade="all"/>
<subclass name="com.rc.model.mexp.AdminMerchantUserAccount" discriminator-value="true"/>
</class>
</hibernate-mapping>
Is this even possible?

Hibernate one-to-one XML mapping

I found the following links:
Hibernate one to one mapping problem
Hibernate one-to-one mapping with a reference column (XML mapping)
hibernate one-to-one (on foreign key) vs one-to-one (on primary key)
But nothing seems to work.
I have 2 entities:
class User {
Integer userId;
Profile userProfile;
}
class Profile {
Integer profileId;
User user;
}
With XML mapping:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="model.User" table="User" catalog="Proj1" dynamic-update="true">
<id name="userId" type="java.lang.Integer">
<column name="userId" />
<generator class="identity" />
</id>
<one-to-one name="userProfile" class="model.Profile">
</one-to-one>
</class>
</hibernate-mapping>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- Generated Jun 12, 2013 7:51:22 PM by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
<class name="model.Profile" table="Profile" catalog="Proj1" dynamic-update="true">
<id name="profileId" type="java.lang.Integer">
<column name="profileId" />
<generator class="identity" />
</id>
<many-to-one name="user" class="model.Users" unique="true">
<column name="userId" />
</many-to-one>
</class>
</hibernate-mapping>
The thing here is, the User must have exactly one Profile but the Profile doesn't necessarily to have one User so a Profile may have null User.
Now the problem is every time I fetch a User with its associated Profile, the Profile retrieved is the Profile with the profileId the same as userId, that is if the User has userId 4 the Profile retrieved is the profile with profileId 4 as well even though it is supposed to retrieve Profile with userId 4 not profileId 4.
Update: add Dao code
public User findById( int id ) {
log.debug("getting User instance with id: " + id);
try {
Criteria userCriteria = this.sessionFactory.getCurrentSession().createCriteria(User.class);
userCriteria.add(Restrictions.idEq(id));
userCriteria.setFetchMode("userProfile", FetchMode.JOIN);
userCriteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
Users instance = (Users) userCriteria.uniqueResult();
if(instance == null)
log.debug("get successful, no instance found");
else
log.debug("get successful, instance found");
return instance;
}
catch(RuntimeException re) {
log.error("get failed", re);
throw re;
}
}
Finally I found the solution. Originally I had to set the userProfile manually every time I need to fetch the associated userProfile of a User just for a temporary workaround. But I just found this link: http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/associations.html#assoc-bidirectional-121
So basically I just have to add unique="true" not-null="false" to the many-to-one of the user in Profile xml and add property-ref="user" to the one-to-one userProfile in User. I think the key here is the property-ref="user"

Can not get list object which have one-to-many relationship in hibernate using mysql

I have schema below:
Customer
id -Primary key
customer_name
phone
.............
Service
id -Primary key
customer_id -- foreign key to Customer.id
.................
Customer.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="thesis.database.Customer" table="customer">
<meta attribute="class-description">
</meta>
<id name="customerId" type="int" column="customer_id">
<generator class="native" />
</id>
<property name="phone" column="phone" type="string" />
<bag name="services" table="use_service" inverse="false" fetch="join" lazy="false"
cascade="all">
<key column="customer_id" />
<one-to-many class="thesis.database.Service" />
</bag>
</class>
Service.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="thesis.database.Service" table="service">
<meta attribute="class-description">
This class contains the Service detail.
</meta>
<id name="id" type="int" column="id">
<generator class="native" />
</id>
<many-to-one name="customer" class="thesis.database.Customer"
fetch="select">
<column name="customer_id" not-null="true" />
</many-to-one>
....................
</class>
My function
public static Customer getCustomerByPhoneNumber(String phoneNumber) {
Session session = getSession();
Criteria criteria = session.createCriteria(Customer.class);
Criterion phone = Restrictions.eq("phone", phoneNumber);
criteria.add(phone);
Customer customer = (Customer) criteria.uniqueResult();
session.close();
return customer;
}
And after that, i call
Customer customer = getCustomerByPhoneNumber("123456789"); // customer with this phone is availuable in database
I get this customer normally, but i cell getServices() function to get list service, it always get the same list, although i try to add more records to service table.
For example:
Customer table
id customer_name phone ................
1 Mr A 123456789................
and service table
id customer_id ........................
1 1 ........................
2 1 ........................
3 1 ........................
First query. i got list size = 3;
after insert one more record like that 4 1 ............ to service table
Second query. i also got list size = 3;
Can everybody tell me why and suggest any solution? thank in advance!
My solution is using Transaction to commit after adding new record.
Maybe you are forgetting to execute the criteria. Here http://docs.jboss.org/hibernate/orm/3.6/javadocs/org/hibernate/Criteria.html they are always calling the list method.

Categories