As we work, we invent new tools, new ideas. Being associated with this "Information technology / Software Industry" for about 5 years now, would like to share a few tools and ideas, developed out of interest or needs. Therefore this blog.

Sunday, February 22, 2009

Hibernate's dirty check and mutables.

Consider a sample table with the following definition:

create table SHOPPING_ORDER (
ID BIGINT NOT NULL AUTO_INCREMENT,
NAME VARCHAR(255) NOT NULL,
DESCRIPTION VARCHAR(255) NOT NULL,
PLACED_DATE TIMESTAMP NOT NULL,
PRIMARY KEY(ID)
);

The class for the data object of hibernate would look like:
// Generated Feb 22, 2009 6:51:39 PM by Hibernate Tools 3.2.1.GA
import static javax.persistence.GenerationType.IDENTITY;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

/**
* ShoppingOrder generated by hbm2java
*/
@Entity
@Table(name = "shopping_order", catalog = "blog")
public class ShoppingOrder implements java.io.Serializable {

/**
*
*/
private static final long serialVersionUID = -5795228501571594686L;

private Long id;
private String name;
private String description;
private Date placedDate;

public ShoppingOrder() {
}

public ShoppingOrder(String name, String description, Date placedDate) {
this.name = name;
this.description = description;
this.placedDate = placedDate;
}

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "ID", unique = true, nullable = false)
public Long getId() {
return this.id;
}

public void setId(Long id) {
this.id = id;
}

@Column(name = "NAME", nullable = false)
public String getName() {
return this.name;
}

public void setName(String name) {
this.name = name;
}

@Column(name = "DESCRIPTION", nullable = false)
public String getDescription() {
return this.description;
}

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

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "PLACED_DATE", nullable = false, length = 19)
public Date getPlacedDate() {
return this.placedDate;
}

public void setPlacedDate(Date placedDate) {
this.placedDate = placedDate;
}

}

Now lets try to create new shopping orders and save them to the database:

public class ShoppingOrders {
private Timestamp timestamp = new Timestamp(System.currentTimeMillis());

/**
* @param name
* @param description
* @return
*/
public ShoppingOrder createOrders(String name, String description) {
ShoppingOrder order = new ShoppingOrder();
order.setName(name);
order.setDescription(description);
timestamp.setTime(System.currentTimeMillis());
order.setPlacedDate(timestamp);
return order;
}

/**
* @param args
*/
public static void main(String[] args) {
Session openSession = HibernateUtils.getSessionFactory().openSession();
Transaction beginTransaction = openSession.beginTransaction();

ShoppingOrders orders = new ShoppingOrders();

for (int i = 0; i < 500; i++) {
openSession.save(orders.createOrders("Name" + i, "Description" + i));

}

beginTransaction.commit();
openSession.close();
HibernateUtils.getSessionFactory().close();
}
}




Hmm.., everything looks fine?

If you enable the "hibernate.show_sql" flag to see the SQL queries being executed, you will see SQL updates being fired apart from the SQL inserts which were expected. Oops!, where did these SQL updates come from, we were merely creating new "ShoppingOrder"(s)!

If you have noticed the "java.sql.Timestamp"'s (a mutable class) instance is being shared among all the "ShoppingOrder"(s) that are being created. On each creation and save of a "ShoppingOrder", the "setTime( )" method is being called on the mutable instance.

Say after 'N' "save( )" calls, hibernate flushes the session, on the creation of next "ShoppingOrder" when the "setTime( )" method is called it will make the already attached instances of "ShoppingOrder" dirty! This will trigger the updates which we discovered.

Thumb rule, whenever we associate a mutable instance to a DO's intance, to be on the defensive, we should clone it and associate the copy.

In this case if the method, "setPlacedDate(java.sql.Date)" of the data object "ShoppingOrder" is modified as the following,
public void setPlacedDate(Date placedDate) {
this.placedDate = new Timestamp(placedDate.getTime());
}
then, the problem of updates would get rectified!

We should also do a defensive copy in the constructor of the data object "ShoppingOrder".

public ShoppingOrder(String name, String description, Date placedDate) {
this.name = name;
this.description = description;
this.placedDate = new TimeStamp(placedDate.getTime());
}
Why... :)

1 comment:

  1. This is very good article for understanding Mutable Objects and defensive copy.

    ReplyDelete

Followers

Archives