Wednesday, March 18, 2015

Entity and Value Object and Embedding multiple objects

There are two type of objects in hibernate.
1. Entity
2. Value Objects

Entity
--------
Entity objects basically can stand alone without depending on another.
eg: College, Student

Value Objects
-----------------
Object which cannot standalone and does not have a clear meaning when taken alone. These type of objects has meaning only when they are presented with an Entity Object. In other words these objects add a meaning / value to Entity objects.
eg: Address - address will add a  value to Student or College.

Hibernate gives @Embeddable  annotation to embed the value object to Entity.

Steps to embed
-------------------
1. annotate value object with @Embeddable .
   eg : Address annotate with @Embeddable

2. Define a reference to value object inside Entity.Annotate the reference/ field with @Embedded.
Note: Even though you did not annotate the reference with @Embedded , still value object will embed in to Entity. The table entries for Value Object will be created inside Entity object's table.
Address object's field values will be created inside Studet/College or UserDetails table.
 eg :

@Embedded
private Address address;


Entity

@Entity
@Table(name = "USER_DETAILS")
public class UserDetails {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "USER_ID")
private int userId;
@Basic
private String userName;

@Temporal(TemporalType.TIMESTAMP)
private Date joinDate;
@Transient
private String notInTable;
@Embedded
private Address address;

@Lob
private String description;

public int getUserId() {
return userId;
}

public void setUserId(int userId) {
this.userId = userId;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public Date getJoinDate() {
return joinDate;
}

public void setJoinDate(Date joinDate) {
this.joinDate = joinDate;
}

public String getNotInTable() {
return notInTable;
}

public void setNotInTable(String notInTable) {
this.notInTable = notInTable;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public Address getAddress() {
return address;
}

public void setAddress(Address address) {
this.address = address;
}

@Override
public String toString() {
return "UserDetails [userId=" + userId + ", userName=" + userName
+ ", joinDate=" + joinDate + ", notInTable=" + notInTable
+ ", address=" + address + ", description=" + description + "]";
}
}

Value Objects
-------

@Embeddable
public class Address {
private String street;
private String city;
private String state;
private String pincode;

public String getStreet() {
return street;
}

public void setStreet(String street) {
this.street = street;
}

public String getCity() {
return city;
}

public void setCity(String city) {
this.city = city;
}

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

public String getPincode() {
return pincode;
}

public void setPincode(String pincode) {
this.pincode = pincode;
}

@Override
public String toString() {
return "Address [street=" + street + ", city=" + city + ", state="
+ state + ", pincode=" + pincode + "]";
}


}


Table : user_Details

USER_ID,description,joinDate,userName,city,pincode,state,street
--------------------------------------------------------------------------------------
5,"FU description","2015-03-18 11:04:08","First User",fCity,fPincode,fstate,fstreet
6,"FU description","2015-03-18 11:05:38","First User",fCity,fPincode,fstate,fstreet

Note: I have not defined column names for Embedded object and thus it will create columns with corresponding field names.

So let's give the names for columns
in hibernate configuration file ( hibernate.cfg.xml ) property for gbm2ddl is set as update
<property name="hbm2ddl.auto">create

So when we give the names for columns, it will create new set of columns and enter the values inside USER_DETAIL  table.

public class Address {
@Column(name="STREET_NAME")
private String street;
@Column(name="CITY_NAME")
private String city;
@Column(name="STATE_NAME")
private String state;
@Column(name="PINCODE")
private String pincode;

......getter and setters
}

Table entries

USER_ID,description,joinDate,userName,city,pincode,state,street,CITY_NAME,STATE_NAME,STREET_NAME
-------------------------------------------------------
9,"FU description","2015-03-18 12:43:40","FirstUser",fCity,fPincode,fstate,fstreet,NULL,NULL,NULL
10,"FU description","2015-03-18 12:46:07","FirstUser",NULL,fPincode,NULL,NULL,fCity,fstate,fstreet


As you can see above new columns created with name CITY_NAME,STATE_NAME,STREET_NAME
No new column created for pincode as name has not been changed.

Previous columns has null values.

So if needed to get rid of old columns , all you have to do is to change "hbm2ddl" property value to "create"
But then it will drop the previous schema and create a brand new table and enter values. You will lose old data.

USER_ID,CITY_NAME,PINCODE,STATE_NAME,STREET_NAME,description,joinDate,userName
1,fCity,fPincode,fstate,fstreet,"FU description","2015-03-18 13:04:22","First User"


test class as below
----------------------
import java.util.Date;

import org.dom4j.util.UserDataElement;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import dto.user.Address;
import dto.user.UserDetails;

public class hibernate {
public static void main(String[] args) {
UserDetails user = new UserDetails();
// user.setUserId(1);
user.setUserName("First User");
user.setJoinDate(new Date());
user.setDescription("FU description");

Address address = new Address();
address.setCity("fCity");
address.setPincode("fPincode");
address.setState("fstate");
address.setStreet("fstreet");

user.setAddress(address);
// create session factory - one object- get session from SessionFactory
// need configuration to build sessionFactory
// new Configuration().configure()
// u need to have hibernate.cfg.xml in default place resources

SessionFactory sessionFactory = new Configuration().configure()
.buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(user);
session.getTransaction().commit();
session.close();

// retrieve object

user = null;
session = sessionFactory.openSession();
session.beginTransaction();
user = (UserDetails) session.get(UserDetails.class, 1);
System.out.println(user);
}
}


---------------------------------------------------

Now we have a problem. That is If you need to have more than one value object of same type in Entity.
ie: two addresses, homeAddress and officeAddress

       @Embedded
private Address homeAddress;
@Embedded
private Address officeAddress;

You can try to save the object. It will definitely give an error where address table columns will be duplicated. You cannot have two columns in the same name. Columns should be unique. Error will happen when second address columns comes.

------------ error will be as below -----------------------------

Exception in thread "main" org.hibernate.MappingException: Repeated column in mapping for entity: dto.user.UserDetails column: CITY_NAME (should be mapped with insert="false" update="false")
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:709)
at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:731)
at org.hibernate.mapping.PersistentClass.checkPropertyColumnDuplication(PersistentClass.java:727)
at org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:753)
at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:506)
at org.hibernate.mapping.RootClass.validate(RootClass.java:270)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1360)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1851)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1930)
at hibernate.main(hibernate.java:39)

----------------------------------------------------------------


Answer:

How to overcome this issue in the Embedded objects. Hibernate has given option to override.
We have two annotations
@AttributeOverride =  override the name of a column
@AttributeOverrides =  group override columns to embed object

So we have to give the override names with @AttributeOverride to each attribute/ field in the address table , and finally all @AttributeOverride  definitions must be grouped inside @AttributeOverrides.

So when wrtting the annotation , best thing is write the
@AttributeOverrides   and then write @AttributeOverride inside it one by one for each attribute in the address/ value object with comma (,) seperated within the braces as shown below.

@AttributeOverride  will have atleast
 * name of the attribute we are going to rename , in value object (street)
 * name of the column in the table

So going into the example we will have

@Embedded
@AttributeOverrides(
@AttributeOverride(name = "street", column = @Column(name = "HOME_STREET_NAME")) ,
@AttributeOverride(name="city", column=@Column(name="HOME_STREET_NAME")),
@AttributeOverride(name="state", column=@Column(name="HOME_STREET_NAME")),
@AttributeOverride(name="pincode", column=@Column(name="HOME_STREET_NAME"))
})
private Address homeAddress;


Note : As per our example we are using only two address object and giving override values for either homeAddress or OfficeAddress is enough as other object will have table column names with default address attribute defiend column names.
Annotation will be written in the Entity object (UserDetails)

eg : here if we give override values for only honeAddress as shown above then we will have table entries

-------------------------

USER_ID,description,HOME_CITY_NAME,HOME_PINCODE,HOME_STATE_NAME,HOME_STREET_NAME,joinDate,CITY_NAME,PINCODE,STATE_NAME,STREET_NAME,userName
--------------------------------------------------------------------------------------------------------
1,"FU description",hCity,hPincode,hstate,hstreet,
"2015-03-18 13:51:42",oCity,oPincode,ostate,ostreet,"First User"

-----------------------------------------

But it is always better to give attribute override definition to both value objects. home address and office address.

So new code will be as follows

Address.java
----------------------

import javax.persistence.Column;
import javax.persistence.Embeddable;

@Embeddable
public class Address {
@Column(name="STREET_NAME")
private String street;
@Column(name="CITY_NAME")
private String city;
@Column(name="STATE_NAME")
private String state;
@Column(name="PINCODE")
private String pincode;

public String getStreet() {
return street;
}

public void setStreet(String street) {
this.street = street;
}

public String getCity() {
return city;
}

public void setCity(String city) {
this.city = city;
}

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

public String getPincode() {
return pincode;
}

public void setPincode(String pincode) {
this.pincode = pincode;
}

@Override
public String toString() {
return "Address [street=" + street + ", city=" + city + ", state="
+ state + ", pincode=" + pincode + "]";
}


}


--------------------------------------------------------

UserDetails.java
-----------------------------------------------------------

import java.util.Date;

import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

@Entity
@Table(name = "USER_DETAILS")
public class UserDetails {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "USER_ID")
private int userId;
@Basic
private String userName;

@Temporal(TemporalType.TIMESTAMP)
private Date joinDate;
@Transient
private String notInTable;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "street", column = @Column(name = "HOME_STREET_NAME")),
@AttributeOverride(name = "city", column = @Column(name = "HOME_CITY_NAME")),
@AttributeOverride(name = "state", column = @Column(name = "HOME_STATE_NAME")),
@AttributeOverride(name = "pincode", column = @Column(name = "HOME_PINCODE")) })
private Address homeAddress;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "street", column = @Column(name = "OFFICE_STREET_NAME")),
@AttributeOverride(name = "city", column = @Column(name = "OFFICE_CITY_NAME")),
@AttributeOverride(name = "state", column = @Column(name = "OFFICE_STATE_NAME")),
@AttributeOverride(name = "pincode", column = @Column(name = "OFFICE_PINCODE")) })
private Address officeAddress;

@Lob
private String description;

public int getUserId() {
return userId;
}

public void setUserId(int userId) {
this.userId = userId;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public Date getJoinDate() {
return joinDate;
}

public void setJoinDate(Date joinDate) {
this.joinDate = joinDate;
}

public String getNotInTable() {
return notInTable;
}

public void setNotInTable(String notInTable) {
this.notInTable = notInTable;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public Address getHomeAddress() {
return homeAddress;
}

public void setHomeAddress(Address homeAddress) {
this.homeAddress = homeAddress;
}

public Address getOfficeAddress() {
return officeAddress;
}

public void setOfficeAddress(Address officeAddress) {
this.officeAddress = officeAddress;
}

@Override
public String toString() {
return "UserDetails [userId=" + userId + ", userName=" + userName
+ ", joinDate=" + joinDate + ", notInTable=" + notInTable
+ ", homeAddress=" + homeAddress + ", officeAddress="
+ officeAddress + ", description=" + description + "]";
}
}


----------------------------

test class
hibernateT.java


-----------------------------------------------------

import java.util.Date;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import dto.user.Address;
import dto.user.UserDetails;

public class hibernate {
public static void main(String[] args) {
UserDetails user = new UserDetails();
// user.setUserId(1);
user.setUserName("First User");
user.setJoinDate(new Date());
user.setDescription("FU description");

Address homeAddress = new Address();
homeAddress.setCity("hCity");
homeAddress.setPincode("hPincode");
homeAddress.setState("hstate");
homeAddress.setStreet("hstreet");

Address officeAddress = new Address();
officeAddress.setCity("oCity");
officeAddress.setPincode("oPincode");
officeAddress.setState("ostate");
officeAddress.setStreet("ostreet");

user.setHomeAddress(homeAddress);
user.setOfficeAddress(officeAddress);
// create session factory - one object- get session from SessionFactory
// need configuration to build sessionFactory
// new Configuration().configure()
// u need to have hibernate.cfg.xml in default place resources

SessionFactory sessionFactory = new Configuration().configure()
.buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(user);
session.getTransaction().commit();
session.close();

// retrieve object

user = null;
session = sessionFactory.openSession();
session.beginTransaction();
user = (UserDetails) session.get(UserDetails.class, 1);
System.out.println(user);
}
}

---------------------------------------

output
------------------------------------

INFO: HHH000227: Running hbm2ddl schema export
Hibernate: drop table if exists USER_DETAILS
Hibernate: create table USER_DETAILS (USER_ID integer not null auto_increment, description longtext, HOME_CITY_NAME varchar(255), HOME_PINCODE varchar(255), HOME_STATE_NAME varchar(255), HOME_STREET_NAME varchar(255), joinDate datetime, OFFICE_CITY_NAME varchar(255), OFFICE_PINCODE varchar(255), OFFICE_STATE_NAME varchar(255), OFFICE_STREET_NAME varchar(255), userName varchar(255), primary key (USER_ID))
Mar 18, 2015 1:59:43 PM org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: HHH000230: Schema export complete
Hibernate: insert into USER_DETAILS (description, HOME_CITY_NAME, HOME_PINCODE, HOME_STATE_NAME, HOME_STREET_NAME, joinDate, OFFICE_CITY_NAME, OFFICE_PINCODE, OFFICE_STATE_NAME, OFFICE_STREET_NAME, userName) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: select userdetail0_.USER_ID as USER_ID1_0_0_, userdetail0_.description as descript2_0_0_, userdetail0_.HOME_CITY_NAME as HOME_CIT3_0_0_, userdetail0_.HOME_PINCODE as HOME_PIN4_0_0_, userdetail0_.HOME_STATE_NAME as HOME_STA5_0_0_, userdetail0_.HOME_STREET_NAME as HOME_STR6_0_0_, userdetail0_.joinDate as joinDate7_0_0_, userdetail0_.OFFICE_CITY_NAME as OFFICE_C8_0_0_, userdetail0_.OFFICE_PINCODE as OFFICE_P9_0_0_, userdetail0_.OFFICE_STATE_NAME as OFFICE_10_0_0_, userdetail0_.OFFICE_STREET_NAME as OFFICE_11_0_0_, userdetail0_.userName as userNam12_0_0_ from USER_DETAILS userdetail0_ where userdetail0_.USER_ID=?
UserDetails [userId=1, userName=First User, joinDate=2015-03-18 13:59:42.0, notInTable=null, homeAddress=Address [street=hstreet, city=hCity, state=hstate, pincode=hPincode], officeAddress=Address [street=ostreet, city=oCity, state=ostate, pincode=oPincode], description=FU description]


---------------------------------------------------------

Table Entries = user_Details

--------------------------------------------------------------

USER_ID,description,HOME_CITY_NAME,HOME_PINCODE,HOME_STATE_NAME,HOME_STREET_NAME,joinDate,OFFICE_CITY_NAME,OFFICE_PINCODE,OFFICE_STATE_NAME,OFFICE_STREET_NAME,userName
--------------------------------------------
1,"FU description",hCity,hPincode,hstate,hstreet,"2015-03-18 13:59:42",oCity,oPincode,ostate,ostreet,"First User"


Problem 3:
How to make embedded object as primary key

Let's say we have a value object LoginName and we want to make this as a primary key for UserDetail.

So let's change userName of type int to LoginName

before change;;
--------

        @Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "USER_ID")
private int userId;

----

Note : 
Now we can't use @Id , we have convert this to @EmbeddedId and have to remove column definition also. cause LoginName will have few fields.

New 
--------

@EmbeddedId
private LoginName userId;

Note : making LoginName class @Embeddable is optional

@Embeddable
public class LoginName implements Serializable {

}


Final code for Embedded primary key
-----------------------------------------------------

Address.java  - no change

UserDetails.java 

change primary key type int to LoginName
remove annotation and changed to @EmbeddedId

------------------------------------------------------------------------
import java.util.Date;

import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

@Entity
@Table(name = "USER_DETAILS")
public class UserDetails {

@EmbeddedId
private LoginName userId;
@Basic
private String userName;

@Temporal(TemporalType.TIMESTAMP)
private Date joinDate;
@Transient
private String notInTable;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "street", column = @Column(name = "HOME_STREET_NAME")),
@AttributeOverride(name = "city", column = @Column(name = "HOME_CITY_NAME")),
@AttributeOverride(name = "state", column = @Column(name = "HOME_STATE_NAME")),
@AttributeOverride(name = "pincode", column = @Column(name = "HOME_PINCODE")) })
private Address homeAddress;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "street", column = @Column(name = "OFFICE_STREET_NAME")),
@AttributeOverride(name = "city", column = @Column(name = "OFFICE_CITY_NAME")),
@AttributeOverride(name = "state", column = @Column(name = "OFFICE_STATE_NAME")),
@AttributeOverride(name = "pincode", column = @Column(name = "OFFICE_PINCODE")) })
private Address officeAddress;

@Lob
private String description;

public LoginName getUserId() {
return userId;
}

public void setUserId(LoginName userId) {
this.userId = userId;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public Date getJoinDate() {
return joinDate;
}

public void setJoinDate(Date joinDate) {
this.joinDate = joinDate;
}

public String getNotInTable() {
return notInTable;
}

public void setNotInTable(String notInTable) {
this.notInTable = notInTable;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public Address getHomeAddress() {
return homeAddress;
}

public void setHomeAddress(Address homeAddress) {
this.homeAddress = homeAddress;
}

public Address getOfficeAddress() {
return officeAddress;
}

public void setOfficeAddress(Address officeAddress) {
this.officeAddress = officeAddress;
}

@Override
public String toString() {
return "UserDetails [userId=" + userId + ", userName=" + userName
+ ", joinDate=" + joinDate + ", notInTable=" + notInTable
+ ", homeAddress=" + homeAddress + ", officeAddress="
+ officeAddress + ", description=" + description + "]";
}
}
---------------------------------------------------------------------------------------------------

LoginName.java  - newly created , implements Serializable
Note : @Embeddable is optional, as we mention @EmbeddedId  in the UserDetails.java 

@EmbeddedId
private LoginName userId;


----------------------------------------------------------------------------------------------------

import javax.persistence.Embeddable;

@Embeddable
public class LoginName implements Serializable {

/**
*/
private static final long serialVersionUID = -121704761597041786L;
private String firstName;
private String lastname;

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastname() {
return lastname;
}

public void setLastname(String lastname) {
this.lastname = lastname;
}

}



----------------------------------------------------------------------------------------------------
Test code

----------------------------------------------------------------------------------------------------
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import dto.user.Address;
import dto.user.LoginName;
import dto.user.UserDetails;

public class hibernate {
public static void main(String[] args) {
UserDetails user = new UserDetails();
// user.setUserId(1);
LoginName loginName = new LoginName();
loginName.setFirstName("firstName1");
loginName.setLastname("lastname");
user.setUserId(loginName);
user.setUserName("First User");
user.setJoinDate(new Date());
user.setDescription("FU description");

Address homeAddress = new Address();
homeAddress.setCity("hCity");
homeAddress.setPincode("hPincode");
homeAddress.setState("hstate");
homeAddress.setStreet("hstreet");

Address officeAddress = new Address();
officeAddress.setCity("oCity");
officeAddress.setPincode("oPincode");
officeAddress.setState("ostate");
officeAddress.setStreet("ostreet");

user.setHomeAddress(homeAddress);
user.setOfficeAddress(officeAddress);
// create session factory - one object- get session from SessionFactory
// need configuration to build sessionFactory
// new Configuration().configure()
// u need to have hibernate.cfg.xml in default place resources

SessionFactory sessionFactory = new Configuration().configure()
.buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(user);
session.getTransaction().commit();
session.close();

// retrieve object

user = null;
session = sessionFactory.openSession();
session.beginTransaction();
// user = (UserDetails) session.get(UserDetails.class, 1);
// System.out.println(user);
}
}


----------------------------------------------------------------------------------------------------
output for update

----------------------------------------------------------------------------------------------------

INFO: HHH000232: Schema update complete
Hibernate: insert into USER_DETAILS (description, HOME_CITY_NAME, HOME_PINCODE, HOME_STATE_NAME, HOME_STREET_NAME, joinDate, OFFICE_CITY_NAME, OFFICE_PINCODE, OFFICE_STATE_NAME, OFFICE_STREET_NAME, userName, firstName, lastname) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)



output for create - create new schema
-------------

INFO: HHH000227: Running hbm2ddl schema export
Hibernate: drop table if exists USER_DETAILS
Hibernate: create table USER_DETAILS (firstName varchar(255) not null, lastname varchar(255) not null, description longtext, HOME_CITY_NAME varchar(255), HOME_PINCODE varchar(255), HOME_STATE_NAME varchar(255), HOME_STREET_NAME varchar(255), joinDate datetime, OFFICE_CITY_NAME varchar(255), OFFICE_PINCODE varchar(255), OFFICE_STATE_NAME varchar(255), OFFICE_STREET_NAME varchar(255), userName varchar(255), primary key (firstName, lastname))
Mar 18, 2015 4:15:54 PM org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: HHH000230: Schema export complete
Hibernate: insert into USER_DETAILS (description, HOME_CITY_NAME, HOME_PINCODE, HOME_STATE_NAME, HOME_STREET_NAME, joinDate, OFFICE_CITY_NAME, OFFICE_PINCODE, OFFICE_STATE_NAME, OFFICE_STREET_NAME, userName, firstName, lastname) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)



----------------------------------------------------------------------------------------------------



Errors which can come making value object as primary key

1. embedded object as primary key , composite key as primary key not implementing serializable
---------------------------------------------------

Exception in thread "main" org.hibernate.MappingException: Composite-id class must implement Serializable: dto.user.LoginName
at org.hibernate.mapping.RootClass.checkCompositeIdentifier(RootClass.java:290)
at org.hibernate.mapping.RootClass.validate(RootClass.java:279)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1360)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1851)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1930)
at hibernate.main(hibernate.java:45)

Problem: LoginName.java has not implemented Serializable
Solution : implements Serializable

@Embeddable
public class LoginName implements Serializable {

}

2. Duplicate key
--------------------

As we are using composite key, the combination of the fields in the embedded object must be unique.
 eg : Our LoginName include two fields. firstName and lastName. The value combination of first name and last name must be unique.

So if you try to enter same values for first name and last name, you will get duplicate key violation

--------------------------------------------

Exception in thread "main" org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:72)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:126)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:112)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:211)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:62)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3124)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3581)
at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:104)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:465)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:351)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1222)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177)
at hibernate.main(hibernate.java:49)
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'firstName-lastname' for key 'PRIMARY'
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
at com.mysql.jdbc.Util.getInstance(Util.java:381)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1015)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3558)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3490)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1959)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2109)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2643)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2077)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2362)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2280)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2265)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:208)
... 13 more

-------------------------------------------------------------------------

.

3. Null id generation for Embedded key
----------
Here as for example Entity class "UserDetails.java" we have defined @EmbeddedId " LoginName userId;"
when saving system need to generate the id for UserDetails  object.
So in generating id, UserDetails object needs the details of the LoginName object.
If LoginName details are not provided, LoginName object detail is null
LoginName = null
, then UserDetails object cannot generate id., EmbeddedId


--------------------------------------------
Exception in thread "main" org.hibernate.id.IdentifierGenerationException: null id generated for:class dto.user.UserDetails
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:119)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:209)
at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:55)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:194)
at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:49)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:715)
at org.hibernate.internal.SessionImpl.save(SessionImpl.java:707)
at org.hibernate.internal.SessionImpl.save(SessionImpl.java:702)
at hibernate.main(hibernate.java:48)

----------------------------------------

solution :
provide details for LoginName when creating UserDetails object and save

               UserDetails user = new UserDetails();

                LoginName loginName = new LoginName();
loginName.setFirstName("firstName");
loginName.setLastname("lastname");
user.setUserId(loginName);


 4. Constraint violation

This is same as above,( LoginName userId  become null , / or not set -  when not set default will be null)

when generating embedded key and also as defined value object as embedded key, the fields/ or attributes values cannot be null.

you must set the values for the userId ( object of the LoginName), the first name and last name.

-----------------------------------------

WARN: Column 'lastname' cannot be null
Exception in thread "main" org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:72)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:126)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:112)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:211)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:62)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3124)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3581)
at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:104)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:465)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:351)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1222)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177)
at hibernate.main(hibernate.java:49)
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'firstName' cannot be null
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
at com.mysql.jdbc.Util.getInstance(Util.java:381)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1015)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3558)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3490)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1959)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2109)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2643)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2077)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2362)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2280)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2265)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:208)
... 13 more

------------------------------------------------

solution : same as solution for above - create loginName  object and set the unique combination set of values for first name and last name.

                LoginName loginName = new LoginName();
loginName.setFirstName("firstName");
loginName.setLastname("lastname");
user.setUserId(loginName);








No comments:

Post a Comment