Hibernate: Where to map an additional column in a relating table? - java

I have two tables in a one-to-many relationship mapped by Hibernate:
<hibernate-mapping>
<class name="Class1" table="TABLE_1" dynamic-update="true">
<meta attribute="implement-equals">true</meta>
<cache usage="read-write"/>
<id name="id" column="KEY_1" length="5" >
....
</id>
<property name="property1a" column="COLUMN_1A" type="string" length="10" />
<property name="property1b" column="COLUMN_1B" type="string" length="10" />
<set name="properties2Set" table="TABLE_2" lazy="true" fetch="subselect" cascade="all">
<key column="FOREIGN_KEY_1"/>
<composite-element class="Class2">
<property name="property2a" type="string" column="COLUMN_2A"/>
<property name="property2b" type="string" column="COLUMN_2B"/>
<property name="property2c" type="string" column="COLUMN_2C"/>
</composite-element>
</set>
</class>
</hibernate-mapping>
Class1 looks like:
public class Class1
{
//... Here the definitions of property1a, property1b
private Set<Class2> properties2Set = new HashSet<Class2>();
public Set<Class2> getProperties2Set()
{
return properties2Set;
}
public void setProperties2Set(Set<Class2> properties2Set)
{
this.properties2Set = properties2Set;
}
// ... Definitions of equals() and hashCode()
}
Class2 contains properties property2a, property2b, property2c. There is a constraint
CONSTRAINT PK_OLD PRIMARY KEY (FOREIGN_KEY_1, COLUMN_2A, COLUMN_2B, COLUMN_2C)
I want to add to the table the column
COLUMN_ID_NUMBER NUMBER(5) DEFAULT 0 NOT NULL
change the constraint into
CONSTRAINT PK_NEW PRIMARY KEY (FOREIGN_KEY_1, COLUMN_ID_NUMBER)
and generate values of COLUMN_ID_NUMBER in order not to violate the constraint.
Where to add the property that would be mapped into COLUMN_ID_NUMBER and how to generate its values? Is it possible to leave the structure of Class2 unchanged?

Related

Hibernate One-to-Many relationship cascade delete

I am new to Hibernate so please guide me.
I have 2 entities Companies & Employees. One Company should have many employees.
Employees Hibernate Mapping File
<hibernate-mapping>
<class name="com.hibernate.demo.Employees" table="employees">
<meta attribute="class-description">
This class contains the employee detail.
</meta>
<id name="empId" type="int" column="emp_id">
<generator class="native"/>
</id>
<property name="empCId" column="emp_cid" type="int"/>
<property name="empName" column="emp_name" type="string"/>
<property name="empContact" column="emp_contact" type="int"/>
</class>
</hibernate-mapping>
Companies Hibernate Mapping File
<hibernate-mapping>
<class name="com.hibernate.demo.Companies" table="companies" >
<meta attribute="class-description">
This class contains the companies detail.
</meta>
<id name="compId" type="int" column="comp_id">
<generator class="native"/>
</id>
<set name="employees" cascade="all" >
<key column="emp_cid"/>
<one-to-many class="com.hibernate.demo.Employees" />
</set>
<property name="compName" column="comp_name" type="string"/>
<property name="compCity" column="comp_city" type="string"/>
</class>
</hibernate-mapping>
Hibernate Configuration File
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernatedbdemo</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">knowarth</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="hbm2ddl.auto">update</property>
<mapping resource="employees.hbm.xml"/>
<mapping resource="companies.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Simple POJO Classes.
Employees.java
public class Employees {
public Employees(){}
private int empId;
private int empCId;
private String empName;
private int empContact;
//Getter & Setter
}
Companies.java
public class Companies {
public Companies(){}
private int compId;
private String compName;
private String compCity;
private Set<Employees> employees;
//Getter & Setter
}
I want to delete Company Record from the companies table and all the employees from that company should be deleted. But the problem I am facing is that Company Record is deleted by all the Employees Record relative to that Company aren't deleted.
Below is the Delete Code
public class CompanyDao {
Configuration cfg = new Configuration().configure("hibernate.cfg.xml");
SessionFactory sf = cfg.buildSessionFactory();
Session session = sf.openSession();
Companies comp = new Companies();
Scanner compSc = new Scanner(System.in);
public void deleteComp(){
session.beginTransaction();
System.out.println("Enter Company ID to delete it");
int cmp_id = compSc.nextInt();
Companies company = new Companies();
company.setCompId(cmp_id);
session.delete(company);
session.getTransaction().commit();
return;
}
}
You can rely on the database for cascading the DELETE statement, in which case you need to change the mapping to:
<set name="employees" cascade="all" inverse="true" >
<key column="emp_cid" on-delete="cascade" />
<one-to-many class="com.hibernate.demo.Employees" />
</set>
If you don't want to change the mapping, you need to fetch the entity from the database and let Hibernate handle the children deletion:
public void deleteComp(){
session.beginTransaction();
System.out.println("Enter Company ID to delete it");
int cmp_id = compSc.nextInt();
Companies company = session.get(Companies.class, cmp_id);
session.delete(company);
session.getTransaction().commit();
return;
}

Hibernate query to get records from three related tables

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)

Both key column and map-key are PK

How do I create a map property that has both key column and a map key as parts of PK of the detail class?
Like this:
<class entity-name="Person" >
<id name="id"/>
<property name="birthDate" type="date"/>
<map name="names">
<key column="personId"/>
<map-key type="string" column="code"/>
<one-to-many class="PersonName" />
</map>
</class>
<class entity-name="PersonName">
<composite-id>
<key-many-to-one name="personId" class="Person"/>
<key-property name="code" type="string" length="32"/>
</composite-id>
<property name="lastName" type="string" length="64" index="nameSearch"/>
<property name="firstName" type="string" length="64" index="nameSearch"/>
<property name="middleName" type="string" length="64" index="nameSearch"/>
<property name="isActual" type="boolean"/>
</class>
I need to avoid creating a surrogate key in PersonName class that would require a special handling. Json shown below should be automatically persisted in the DB, inserting and updating detail record when necessary, based on PersonId-code key.
It's natural that "code" property identifies the row together with the person Id.
I tried different combinations of "inverse", "not-null" etc. I admit that I don't fully get how it works. I get different error messages, like:
ERROR: null value in "person" column violates NOT NULL constraint
Details: Erroreous row contains (null, null, lastname, ffffirst, null, null).
or:
org.hibernate.MappingException: Repeated column in mapping for entity:
PersonName column: person (should be mapped with insert="false" update="false")
It should look like this, if represented as json:
{
"birthDate": "33-44-55",
"names": {
"mainName": {
"lastName": "lastname",
"firstName": "ffffirst"
},
"maidenName": {
"lastName": "lastname1",
"firstName": "ffffirst2"
},
"old": {
"lastName": "lastname3",
"firstName": "ffffirst4"
}
}
}
UPD (clarification of question)
Actually this is a main goal - the system should create master and all details when posted with this json. This json is converted into the mapped classes. If master has an "id" populated, the system should update both master and details, adding new entries when needed. Details ("names" map entries) should not have any surrogate "id" specified. They are identified by master's "personId" field and "code" field.
** clarification 2 **
As shown in the example json, the map key is a string, not a composite key. As seen in mapping xml, this is all dynamic mapping. No additional class, that couldn't be instantiated automatically from the json, should be written.
Hope it's possible!
To get the whole picture, the table is generated OK, I like it:
CREATE TABLE personname
(
personid character varying(32) NOT NULL,
code character varying(32) NOT NULL,
lastname character varying(64),
firstname character varying(64),
middlename character varying(64),
isactual boolean,
CONSTRAINT personname_pkey PRIMARY KEY (personid, code),
CONSTRAINT fk_1skg5frawyftx8co9uawhc3r8 FOREIGN KEY (personid)
REFERENCES person (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
I think your mapping should look like this:
You need a nested Key class (e.g. Person$Key, although it can be an outer class too) for the embedded composite id:
public static class Key {
private Long personId;
private String code;
public Key(Long personId, String code) {
this.personId = personId;
this.code = code;
}
public Long getPersonId() {
return personId;
}
public String getCode() {
return code;
}
#Override
public boolean equals(Object obj) {
if (!(obj instanceof Key)) {
return false;
}
Key k = (Key)obj;
return personId.equals(k.personId) && code.equals(k.code);
}
#Override
public int hashCode() {
return 37*personId.hashCode() + code.hashCode();
}
}
Then your map will be like:
private Map<Key,PersonName> names = new HashMap<Key,PersonName>();
The Hibernate mapping:
<class entity-name="Person" >
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="birthDate" type="date"/>
<map name="names" inverse="true">
<composite-map-key class="Person$Key">
<key-property name="personId"/>
<key-property name="code"/>
</composite-map-key>
<one-to-many class="PersonName" />
</map>
</class>
<class entity-name="PersonName">
<composite-id name="id" class="Person">
<key-property name="personId"/>
<key-property name="code"/>
</composite-id>
<many-to-one name="person" class="Person"
insert="false" update="false">
<column name="personId"/>
<column name="code"/>
</many-to-one>
<property name="lastName" type="string" length="64" index="nameSearch"/>
<property name="firstName" type="string" length="64" index="nameSearch"/>
<property name="middleName" type="string" length="64" index="nameSearch"/>
<property name="isActual" type="boolean"/>
</class>
It's impossible. Use mongodb or write your own ORM.

Hibernate: getting not-null property references a null or transient value

I am new to Hibernate. I have two classes "quotation" and "quotation_item"
their database structure is like this
create table quotation
(
quotation_id int primary key auto_increment,
code varchar(20),
client_name varchar(20)
);
create table quotation_item
(
id int primary key auto_increment,
quotation_id int,
item_name varchar(20),
rate int,
qty int,
FOREIGN KEY (quotation_id) REFERENCES quotation(quotation_id)
);
Quotation.hbm.xml is like this,
<hibernate-mapping>
<class name="com.paramatrix.pojo.Quotation" table="quotation">
<id name="quotationId" type="int" column="quotation_id">
<generator class="native" />
</id>
<property name="code" column="code" type="string" />
<property name="clientName" column="client_name" type="string" />
<set name="quotationItem" table="quotation_item" fetch="select" cascade="all">
<key>
<column name="quotation_id" not-null="true" />
</key>
<one-to-many class="com.paramatrix.pojo.QuotationItem" />
</set>
</class>
</hibernate-mapping>
QuotationItem.hbm.xml is like this,
<hibernate-mapping>
<class name="com.paramatrix.pojo.QuotationItem" table="quotation_item">
<id name="id" type="int" column="id">
<generator class="native" />
</id>
<many-to-one name="quotation" class="com.paramatrix.pojo.Quotation" cascade="save-update">
<column name="quotation_id" not-null="true" />
</many-to-one>
<property name="itemName" column="item_name" type="string" />
<property name="rate" column="rate" type="int" />
<property name="qty" column="qty" type="int" />
</class>
</hibernate-mapping>
POJO's are
1>Quotation.java
public class Quotation {
int quotationId;
String code;
String clientName;
Set<QuotationItem> quotationItem=new HashSet<QuotationItem>();
//getter& setter
}
2>QuotationItem.java
public class QuotationItem{
int id;
Quotation quotation;
String itemName;
int rate;
int qty;
//getter and setter
}
main method is
Set<QuotationItem> set=new HashSet<QuotationItem>();
SessionFactory factory = new Configuration().configure().buildSessionFactory();
Session session=factory.openSession();
session.beginTransaction();
Quotation quotation = new Quotation();
quotation.setCode(txtCode.getValue());
quotation.setClientName(txtClientName.getValue());
QuotationItem quotationItem = new QuotationItem();
quotationItem.setItemName(txtItemName.getValue());
quotationItem.setQty(Integer.parseInt( txtQty.getValue()));
quotationItem.setRate(Integer.parseInt(txtRate.getValue()));
set.add(quotationItem);
quotation.setQuotationItem(set);
session.saveOrUpdate((quotation));
session.getTransaction().commit();
System.out.println("Done");
Try quotationItem.setQuotation(quotation)

Mapping same class relation

Hi
I’m trying to map some classes in hibernate there and have general problem how such mapping can be done.
There is User class and Facebook user class which has the following structure
User Class :
public class User{
public User(){}
Long Id;
String FirstName;
String LastName;
....
FbUser fbuser;
//// all requred
getters and setters...
}
Facebook class FbUser can have list of Friends which are objects of the same class FbUser.
public class FbUser{
public FbUser(){}
Long fbId;
String FirstName;
String LastName;
List<FbUser> friends;
//// all requred
getters and setters...
}
Till now I made many to one relation between User And FbUser.
<hibernate-mapping>
<class
name="User"
table="User"
>
<id
name="Id"
column="ID"
type="java.lang.Long"
unsaved-value="null"
>
<generator class="increment"/>
</id>
<property
name="FirstName"
update="true"
insert="true"
not-null="false"
unique="false"
type="java.lang.String"
>
<column name="FirstName" />
</property>
<property
name="LastName"
update="true"
insert="true"
not-null="false"
unique="false"
type="java.lang.String"
>
<column name="LastName" />
</property>
<many-to-one
name="fbUser"
class="FbUser"
cascade="all"
column="fbId"
unique="true"
/>
</class>
</hibernate-mapping>
And now the FbUser Mapping:
<hibernate-mapping>
<class
name="FbUser"
table="FbUser"
>
<id
name="fbId"
column="fbId"
type="java.lang.Long"
unsaved-value="null"
>
<generator class="increment"/>
</id>
<property
name="FirstName"
update="true"
insert="true"
not-null="false"
unique="false"
type="java.lang.String"
>
<column name="FirstName" />
</property>
<property
name="LastName"
type="java.lang.String"
update="true"
insert="true"
column="LastName"
not-null="true"
unique="false"
/>
</class>
</hibernate-mapping>
Chow can I map FbUser List inside the FbUser Map file? I got lost :(
You can create an additional class named, for instance, MyFriends
public class FbUser {
List<MyFriends> friends = new ArrayList<MyFriends>();
}
Just relevant part
If you have a index-column
<hibernate-mapping>
<class name="FbUser">
<list name="myFriends">
<key column="ME_ID" insert="false" update="false"/>
<list-index column="WHICH COLUMN SHOULD BE USED AS INDEX"/>
<one-to-many class="MyFriends"/>
</list>
</class>
</hibernate-mapping>
If you do not have a index-column
re-write your list as
public class FbUser {
Collection<MyFriends> friends = new ArrayList<MyFriends>();
}
And
<hibernate-mapping>
<class name="FbUser">
<bag name="columns">
<key column="ME_ID" insert="false" update="false"/>
<one-to-many class="MyFriends"/>
</bag>
</class>
</hibernate-mapping>
And your MyFriends mapping. Notice you need a composite primary key (implemented as a static inner class)
<class name="MyFriends">
<composite-id name="myFriendsId" class="MyFriends$MyFriendsId">
<key-property name="meId"/>
<key-property name="myFriendId"/>
</composite-id>
<many-to-one name="me" class="FbUser" insert="false" update="false"/>
<many-to-one name="myFriend" class="FbUser" insert="false" update="false"/>
</class>
Your MyFriends is shown as follows
public class MyFriends {
private MyFriendsId myFrinedId;
private FbUser me;
private FbUser myFriend;
public static class MyFriendsId implements Serializable {
private Integer meId;
private Integer myFriendId;
// getter's and setter's
public MyFriendsId() {}
public MyFriendsId(Integer meId, Integer myFriendId) {
this.meId = meId;
this.myFriendId = myFriendId;
}
// getter's and setter's
public boolean equals(Object o) {
if(!(o instanceof MyFriendsId))
return false;
MyFriendsId other = (MyFriendsId) o;
return new EqualsBuilder()
.append(getMeId(), other.getMeId())
.append(getMyFriendId(), other.getMyFriendId())
.isEquals();
}
public int hashcode() {
return new HashCodeBuilder()
.append(getMeId())
.append(getMyFriendId())
.hashCode();
}
}
}
Well, first: User has a one-to-one relation with FbUser, correct?
second: Map FbUser to FbUser as a many to many relation using a list or a set. I have an Set example here:
<set
name="friends"
table="FbUser" <!-You may use other table here if you want->
access="field">
<key
column="fbId"/>
<many-to-many
class="bla.bla.bla.FbUser"
column="friend_id" />
</set>

Categories