Hibernate query to get records from three related tables - java

I have three objects USER, CONTACT and ACTION.
Each USER has many CONTACTS and each CONTACT has many ACTIONS
Each CONTACT and ACTION has status assigned to them, e.g. 20 or 60 or...
Please have a look at the data model.
Requirement is to get the CONTACTs having a particular status, or get the CONTACTs whose ACTIONs have that particular status.
E.g. get me CONTACTs with status 20, or CONTACTs who’s ACTIONs have status 20
At the moment I have the following query that is retrieving the CONTACTs with the status 20 and does not considers that status of the ACTIONs
USER
public class User {
private Integer userID;
private String userFirstName;
private String userLastName;
private Set<Contact> contactSet = new HashSet<Contact>();
private Set<Action> actionSet = new HashSet<Action>();
private ContactCriteria contactCriteria;
.
.
.
}
CONTACT
public class Contact implements Serializable {
private Integer contactID;
private Integer contactStatus = 0;
private String givenName;
private String familyName;
private String streetAddress;
private Set<User> userSet = new HashSet<User>();
private Set<Action> actionSet = new HashSet<Action>();
.
.
.
}
ACTION
public class Action implements Serializable {
private Integer actionID;
private Integer actionStatus;
private User user;
private String actionNote;
private Contact contact;
.
.
.
}
Following are my mapping files:
User.hbm.xml
<?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.smallworks.model" schema="smallworksdb">
<class name="User" table="USERACCOUNT">
<id column="USER_ID" length="500" name="userID">
<generator class="increment"/>
</id>
<property column="USER_FIRSTNAME" generated="never" lazy="false" length="100" name="userFirstName"/>
<property column="USER_LASTNAME" generated="never" lazy="false" length="100" name="userLastName"/>
<set cascade="all" fetch="select" lazy="true" name="contactSet" sort="unsorted" table="USER_CONTACT">
<key column="USER_ID"/>
<many-to-many class="com.smallworks.model.Contact"
column="CONTACT_ID" order-by="CONTACT_ID" unique="false"/>
</set>
<!-- one to many mapping with Action -->
<set inverse="true" lazy="true" name="actionSet" sort="unsorted" order-by="ACTION_DUE_DATE" cascade="save-update">
<key column="USER_ID"/>
<one-to-many class="com.smallworks.model.Action"/>
</set>
<!-- one to one mapping with ContactCriteria -->
<one-to-one name="contactCriteria" class="com.smallworks.model.ContactCriteria"
cascade="save-update" lazy="false"></one-to-one>
</class>
</hibernate-mapping>
Contact.hbm.xml
<?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.smallworks.model" schema="smallworksdb">
<class name="Contact" table="CONTACT">
<id column="CONTACT_ID" length="500" name="contactID">
<generator class="increment"/>
</id>
<property column="GIVEN_NAME" generated="never" lazy="false"
length="100" name="givenName"/>
<property column="FAMILY_NAME" generated="never" lazy="false"
length="100" name="familyName"/>
<property column="STREET_ADDRESS" generated="never" lazy="false"
length="100" name="streetAddress"/>
<property column="CONTACT_STATUS" generated="never" lazy="false"
name="contactStatus" type="integer"/>
<set inverse="true" lazy="false" name="userSet" sort="unsorted" table="USER_CONTACT">
<key column="CONTACT_ID"/>
<many-to-many class="com.smallworks.model.User" column="USER_ID" unique="false"/>
</set>
<!-- one to many mapping with Action -->
<set inverse="true" lazy="true" name="actionSet" sort="unsorted" order-by="ACTION_DUE_DATE" cascade="save-update">
<key column="CONTACT_ID"/>
<one-to-many class="com.smallworks.model.Action"/>
</set>
</class>
</hibernate-mapping>
Action.hbm.xml
<?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.smallworks.model" schema="smallworksdb">
<class name="Action" table="ACTION">
<id column="ACTION_ID" length="500" name="actionID">
<generator class="increment"/>
</id>
<property column="ACTION_STATUS" generated="never" lazy="false"
name="actionStatus" type="integer"/>
<!-- many to one mapping with Contact -->
<many-to-one cascade="save-update"
class="com.smallworks.model.Contact" column="CONTACT_ID" lazy="false"
name="contact" not-null="true" />
<!-- many to one mapping with User -->
<many-to-one class="com.smallworks.model.User" column="USER_ID"
lazy="false" name="user" not-null="true"/>
</class>
</hibernate-mapping>
My existing query is:
Query query = session.createQuery("select distinct c FROM com.smallworks.model.User as u INNER JOIN u.contactSet as c WHERE u.userID=:userIDPara AND c.contactStatus in (:contactStatusPara)");
query.setParameter("userIDPara", user.getUserID());
query.setParameterList("contactStatusPara", statusList);
contactList = query.list();

Add an outer join on c.actions as a and an OR restriction on a.status.
select distinct c FROM com.smallworks.model.User as u INNER JOIN u.contactSet as c LEFT OUTER JOIN c.actionSet a WHERE u.userID=:userIDPara AND (c.contactStatus in (:contactStatusPara) OR a.actionStatus in (: actionStatusPara)

Related

What is a proper way to delete a list of objects in Hibernate?

I have an object User, which has a List of UserDictionary. Each UserDictionary has a List of UserWords with cascade delete.
User.java
public class User implements Serializable
{
private Long id;
private String login;
private String password;
private String email;
private Boolean isVerified;
private List<UserDictionary> dictionaries = new ArrayList<>();
private UserTrainingSettings settings;
//getters and setters there
}
And mapping User.hbm.xml
<hibernate-mapping>
<class name="User" dynamic-insert="true" dynamic-update="true" table="USER" entity-name="user">
<id name="id" type="java.lang.Long" column="user_id">
<generator class="native"/>
</id>
<property name="login" type="java.lang.String" length="40" not-null="true" unique="true"/>
<property name="password" type="java.lang.String" length="32" not-null="true"/>
<property name="email" type="java.lang.String" length="100" not-null="true"/>
<property name="isVerified" type="java.lang.Boolean" column="verified" not-null="true"/>
<list name="dictionaries" fetch="join">
<key column="user_id" not-null="true" on-delete="cascade"/>
<one-to-many class="com.github.wordsmemoriser.Model.UserDictionary"/>
</list>
<one-to-one name="settings" class="com.github.wordsmemoriser.Model.UserTrainingSettings"
cascade="javax.persistence.CascadeType.REMOVE"/>
</class>
</hibernate-mapping>
UserDictionary.java
public class UserDictionary
{
private Long id;
private User user;
private String name;
private List<UserWords> words = new ArrayList<>();
//getters and setters there
}
And mapping UserDictionary.hbm.xml
<hibernate-mapping>
<class name="UserDictionary" dynamic-insert="true" dynamic-update="true" table="DICTIONARY">
<id name="id" type="java.lang.Long" column="dict_id">
<generator class="native"/>
</id>
<many-to-one name="user" class="com.github.wordsmemoriser.Model.User" fetch="join">
<column name="user_id" not-null="true"/>
</many-to-one>
<property name="name" type="java.lang.String" length="100"/>
<list name="words">
<key column="dict_id" not-null="true" on-delete="cascade"/>
<one-to-many class="com.github.wordsmemoriser.Model.UserWords"/>
</list>
</class>
</hibernate-mapping>
What is a proper way to delete all UserDictionaries from User? Should I iterate this list, delete every UserDictionary from database, then clear this list and update User like this
session.beginTransaction();
for(UserDictionary dict : user.getDictionaries())
{
session.delete(dict);
}
user.getDictionaries().clear();
session.update(user);
session.getTransaction().commit();
or should I just clear User's list and then update it like this
user.getDictionaries().clear();
session.beginTransaction();
session.update(user);
session.getTransaction().commit();

Hibernate simultaneous many-to-many and many-to-one mapping

I have a question about the creation of database schemes by Hibernate.
In our project we have users and groups. Groups have exactly one owner and can have multiple members. A user can be owner and member of multiple groups.
We came up with the following Hibernate mapping:
<?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="model.User" table="ProductUser">
<id name="id" column="userId">
<generator class="native"/>
</id>
<property name="email"/>
<property name="firstName"/>
<property name="lastName"/>
<set name="ownedUserGroups" table="UserGroup" inverse="true">
<key column="userId"/>
<one-to-many class="model.UserGroup"/>
</set>
<set name="userGroups" table="Members" inverse="false" lazy="true" fetch="select">
<key column="userId"/>
<many-to-many column="userGroupId" class="model.UserGroup"/>
</set>
</class>
<class name="model.UserGroup" table="UserGroup">
<id name="id" column="userGroupId">
<generator class="native"/>
</id>
<property name="name"/>
<many-to-one name="owner" class="model.User" column="owner_UserId"/>
<set name="members" table="Members" inverse="true" lazy="true" fetch="select">
<key column="userGroupId"/>
<many-to-many column="userId" class="model.User"/>
</set>
</class>
</hibernate-mapping>
The database scheme Hibernate creates for us looks the following:
Database scheme created by Hibernate
Can somebody explain why usergroup has the userid as a foreign key? (As you can see in the image)
For the sake of completeness, here is the code for User and UserGroup:
public class User {
private int id;
private String firstName;
private String lastName;
private String email;
private Set<UserGroup> ownedUserGroups;
private Set<UserGroup> userGroups;
public User() {
this.ownedUserGroups = new HashSet<>();
this.userGroups = new HashSet<>();
}
public User(String firstName, String lastName, String email) {
this();
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
// getters and setters
}
public class UserGroup {
private int id;
private String name;
private User owner;
private Set<User> members;
public UserGroup() {
this.members = new HashSet<>();
}
public UserGroup(String name, User owner, HashSet<User> members) {
this.name = name;
this.owner = owner;
this.members = members;
}
// getters and setters
}
Ok. The problem is with your many-to-one-mapping. What your doing is your trying to set ownedUserGroups to all the groups where the userId equals the id of the current user. However you need to look for all groups where the owner_UserId equals the id of your user. Basically you just need to replace userId with owner_UserId. Your final mapping file should look like this:
<?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="model.User" table="ProductUser">
<id name="id" column="userId">
<generator class="native"/>
</id>
<property name="email"/>
<property name="firstName"/>
<property name="lastName"/>
<set name="ownedUserGroups" table="UserGroup" inverse="true">
<key column="owner_userid"/> <!-- CHANGED -->
<one-to-many class="model.UserGroup"/>
</set>
<set name="userGroups" table="Members" inverse="false" lazy="true" fetch="select">
<key column="userId"/>
<many-to-many column="userGroupId" class="model.UserGroup"/>
</set>
</class>
<class name="model.UserGroup" table="UserGroup">
<id name="id" column="userGroupId">
<generator class="native"/>
</id>
<property name="name"/>
<many-to-one name="owner" class="model.User" column="owner_UserId"/>
<set name="members" table="Members" inverse="true" lazy="true" fetch="select">
<key column="userGroupId"/>
<many-to-many column="userId" class="model.User"/>
</set>
</class>
</hibernate-mapping>

Hibernate 1 to M restriction on children

I have 1 to M association with Country and Person. Meaning a Country can have multiple persons. The country.hbm.xml fils is shown below:
<?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.test.hibernate">
<class name="Country">
<id name="countryId" column="CountryID" > </id>
<property name="countryName" column="CountryName" length="50"></property>
<set name="persons" table="Person" fetch="select" inverse="true">
<key>
<column name="CountryId" not-null="true"></column>
</key>
<one-to-many class="com.test.hibernate.Person"/>
</set>
</class>
</hibernate-mapping>
The Person.hbm.xml is shown below
<?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.test.hibernate">
<class name="Person">
<id name="personID" column="PersonID" > </id>
<property name="name" column="Name" length="50"></property>
<property name="age" column="Age"></property>
<property name="gender" column="Gender" length="1"></property>
<property name="email" column="Email" length="50"></property>
<property name="countryID" column="CountryID" insert="false" update="false"></property>
<many-to-one name="Country" class="com.test.hibernate.Country" fetch="select">
<column name="CountryID" not-null="true"></column>
</many-to-one>
</class>
</hibernate-mapping>
Now, I am trying to query all the persons who are males belonging to India Country
Criteria countryCriteria = session.createCriteria(Country.class);
Criterion country = Restrictions.eq("countryName", "India");
Criterion male = Restrictions.eq("persons.gender", "M");
countryCriteria.add(country);
countryCriteria.add(male);
List<Country> countryList = countryCriteria.list();
I am getting a
Exception in thread "main" org.hibernate.QueryException: could not resolve property: persons.gender of: com.test.hibernate.Country
at org.hibernate.persister.entity.AbstractPropertyMapping.propertyException(AbstractPropertyMapping.java:83)
at org.hibernate.persister.entity.AbstractPropertyMapping.toColumns(AbstractPropertyMapping.java:98)
at org.hibernate.persister.entity.BasicEntityPropertyMapping.toColumns(BasicEntityPropertyMapping.java:61)
at org.hibernate.persister.entity.AbstractEntityPersister.toColumns(AbstractEntityPersister.java:1960)
at org.hibernate.loader.criteria.CriteriaQueryTranslator.getColumns(CriteriaQueryTranslator.java:523)
at org.hibernate.loader.criteria.CriteriaQueryTranslator.findColumns(CriteriaQueryTranslator.java:538)
at org.hibernate.criterion.SimpleExpression.toSqlString(SimpleExpression.java:66)
at org.hibernate.loader.criteria.CriteriaQueryTranslator.getWhereCondition(CriteriaQueryTranslator.java:419)
at org.hibernate.loader.criteria.CriteriaJoinWalker.(CriteriaJoinWalker.java:123)
at org.hibernate.loader.criteria.CriteriaJoinWalker.(CriteriaJoinWalker.java:92)
at org.hibernate.loader.criteria.CriteriaLoader.(CriteriaLoader.java:95)
at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1643)
at org.hibernate.internal.CriteriaImpl.list(CriteriaImpl.java:374)
at com.test.hibernate.Main.main(Main.java:54)
Please help. I am new to Hibernate.
Thanks in advance.
Country.personsis of type Collection<Person>. A Collection doesn't have any property named "gender".
If you used HQL instead of Criteria (and you should, for such a simple static query), you would have to do a join:
select c from Country c
join country.persons person
where c.countryName = 'India'
and person.gender = 'M'
You thus have to do the same with the Criteria query:
Criteria countryCriteria = session.createCriteria(Country.class, "c");
countryCriteria.createALias("c.persons", "person");
countryCriteria.add(Restrictions.eq("c.countryName", "India"));
countryCriteria.add(Restrictions.eq("person.gender", "M"));
List<Country> countryList = countryCriteria.list();
I used M to 1 and used Criteria on Person object as opposed to Country Object.
Criteria personCriteria = session.createCriteria(Person.class,"p");
personCriteria.createAlias("p.Country", "c");
Criterion gender = Restrictions.eq("gender", "M");
Criterion country = Restrictions.eq("c.countryName", "India");
personCriteria.add(gender);
personCriteria.add(country);
List<Person> personList = personCriteria.list();
This way the personList had all the Persons who were males and belonged to India.

Hibernate many-to-many data retrieval

I have two objects User and Contact, with many to many relation, and I am using an intermediate table for this relation USER_CONTACT
Saving the data in this association is fine, but the retrieval is an issue.
I need to retrieve the data based on the User, but what I am getting is all the Contacts, for all the Users.
It will be good if you can let me know what wrong I am doing.
public class User {
private Integer userID;
private String userLoginEmail;
private String password;
private Set<Contact> contactSet = new HashSet<Contact>();
.
.
}
public class Contact implements Serializable {
private Integer contactID;
private String givenName;
private String familyName;
private Set<User> userSet = new HashSet<User>();
.
.
}
User.hbm.xml:
<class name="User" table="USERACCOUNT">
<id column="USER_ID" length="500" name="userID">
<generator class="increment" />
</id>
<property column="USER_LOGIN_EMAIL" generated="never" lazy="false" length="100" name="userLoginEmail" />
<property column="USER_FIRSTNAME" generated="never" lazy="false" length="100" name="userFirstName" />
<property column="USER_LASTNAME" generated="never" lazy="false" length="100" name="userLastName" />
<set name="contactSet" table="USER_CONTACT" inverse="false" lazy="false" fetch="select" cascade="all">
<key column="USER_ID"/>
<many-to-many column="CONTACT_ID" class="com.smallworks.model.Contact"/>
</set>
</class>
Contact.hbm.xml
<class name="Contact" table="CONTACT">
<id column="CONTACT_ID" length="500" name="contactID">
<generator class="increment"/>
</id>
<property column="GIVEN_NAME" generated="never" lazy="false" length="100" name="givenName"/>
<property column="FAMILY_NAME" generated="never" lazy="false" length="100" name="familyName"/>
<!-- many to many mapping with the User via User_Contact table -->
<set inverse="true" lazy="false" name="userSet" sort="unsorted" table="USER_CONTACT">
<key column="USER_ID"/>
<many-to-many class="com.smallworks.model.Contact" column="CONTACT_ID" unique="false"/>
</set>
</class>
and this is how I am trying to retrieve the data, which I think is not correct.
List contactList = session.createQuery("from Contact").list();
It will be good if I can know how to go about getting the Contacts based on the User.
// First, retrieve the user you want.
User user = (User) session.get(User.class, user_id_you_want);
// Second, get the contacts of that given user and add them to a list (optional)
List contacts = new ArrayList();
contacts.addAll(user.getContactSet());
return contacts;

Hibernate : Reading hbm.xml

I am following this titorial on roseindia to get basics of Hibernate : "http://roseindia.net/hibernate/hibernate-update.shtml"
My code is as below and getting the error for the same. Please assist me to fix it!
Java Code:
public class UpdateExample {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Session sess = null;
try {
SessionFactory fact = new Configuration().configure().buildSessionFactory();
sess = fact.openSession();
Transaction tr = sess.beginTransaction();
Insurance ins = (Insurance)sess.get(Insurance.class, new Long(1));
ins.setInsuranceName("Jivan Dhara");
ins.setInvestementAmount(20000);
ins.setInvestementDate(new Date());
sess.update(ins);
tr.commit();
sess.close();
System.out.println("Update successfully!");
}
catch(Exception e){
System.out.println(e.getMessage());
}
}
}
And
public class Insurance {
private String insuranceName;
private double investementAmount;
private Date investementDate;
public String getInsuranceName() {
return insuranceName;
}
public void setInsuranceName(String insuranceName) {
this.insuranceName = insuranceName;
}
public double getInvestementAmount() {
return investementAmount;
}
public void setInvestementAmount(double investementAmount) {
this.investementAmount = investementAmount;
}
public Date getInvestementDate() {
return investementDate;
}
public void setInvestementDate(Date investementDate) {
this.investementDate = investementDate;
}
}
And my contact.hbm.xml :
<?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="Contact" table="CONTACT">
<id name="id" type="long" column="ID" >
<generator class="assigned"/>
</id>
<property name="firstName">
<column name="FIRSTNAME" />
</property>
<property name="lastName">
<column name="LASTNAME"/>
</property>
<property name="email">
<column name="EMAIL"/>
</property>
</class>
<class name="Book" table="book">
<id name="lngBookId" type="long" column="id" >
<generator class="increment"/>
</id>
<property name="strBookName">
<column name="bookname" />
</property>
</class>
<class name="Insurance" table="insurance">
<id name="insuranceName" type="String" column="InsuranceName" >
/>
</id>
<property name="investmentAmount">
<column name="InvestmentAmount" />
</property>
<property name="investmentDate">
<column name="InvestmentDate" />
</property>
</class>
</hibernate-mapping>
And the error i am getting is:
"Error reading resource: contact.hbm.xml"
Also I have created db table by name Insurance with those column fields.
Thanks
Sneha
Isn't something missing in the hbm.xml file? It isn't a complete XML file.
You need to place the hbm.xml file together with the compiled class file for your class.
Is this your whole .hbm.xml file? If so, it's incomplete - it lacks proper structure shown here: http://roseindia.net/hibernate/hibernateormapping.shtml
<?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="roseindia.tutorial.hibernate.Contact" table="CONTACT">
<id name="id" type="long" column="ID" >
<generator class="assigned"/>
</id>
<property name="firstName">
<column name="FIRSTNAME" />
</property>
<property name="lastName">
<column name="LASTNAME"/>
</property>
<property name="email">
<column name="EMAIL"/>
</property>
</class>
</hibernate-mapping>
you have to define two POJO classes
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="Book" table="book" >
<id name="lngBookId" column="id" type="long">
<generator class="increment"></generator>
</id>
<property name="strBookName" type="string">
<column name="bookname" sql-type="VARCHAR2(55)"/>
</property>
</class>
<class name="Insurance" table="insurance" >
<property name="firstName" type="string">
<column name="FIRSTNAME" sql-type="VARCHAR2(55)"/>
</property>
<property name="lastName" type="string">
<column name="LASTNAME" sql-type="VARCHAR2(55)"/>
</property>
<property name="email" type="string">
<column name="EMAIL" sql-type="VARCHAR2(55)"/>
</property>
</class>
</hibernate-mapping>

Categories