Hello again!
Would anyone please give me some tips on how I perform the following. I'm trying to migrate some code from Hibernate2 using Lifecycle interface to Hibernate3 using a DeleteEventListener.
Consider a Order -> Product relationship where I'd like to manage the delete cascade using a DeleteEventListener.
Suppose that when I delete the order I'd like to delete one of two OrderLine objects leaving the not-deleted one de-referenced in the database.
Here's some working code illustrating my problem:
DomainObject's and lifecycle interfaces
Code:
package lifecycle;
public interface Deletable {
public boolean isScheduledForDeletion();
public void handleDelete();
}
Code:
package order;
import java.util.HashSet;
import java.util.Set;
import lifecycle.Deletable;
public class Order implements Deletable {
private Long id;
private String remarks;
private Set<OrderLine> orderLines = new HashSet<OrderLine>();
Order() {
}
public Long getId() {
return this.id;
}
public String getRemarks() {
return this.remarks;
}
public void setRemarks(final String someRemarks) {
this.remarks = someRemarks;
}
public Set<OrderLine> getOrderLines() {
return this.orderLines;
}
public void setOrderLines(final Set<OrderLine> someOrderLines) {
this.orderLines = someOrderLines;
}
public void addOrderLine(final OrderLine aOrderLine) {
final Set<OrderLine> orderLines = this.getOrderLines();
orderLines.add(aOrderLine);
aOrderLine.setOrder(this);
}
public void handleDelete() {
if (this.isScheduledForDeletion()) {
System.out.println("DO delete -> " + this);
} else {
System.out.println("DO NOT delete -> " + this);
}
}
public String toString() {
return this.getClass().getName() + ": id = " + this.getId();
}
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o instanceof Order) {
final Order otherOrder = (Order) o;
return this.getId().equals(otherOrder.getId());
}
return false;
}
public int hashCode() {
final String hashCodeString = this.getId() + this.getRemarks();
return hashCodeString.hashCode();
}
public boolean isScheduledForDeletion() {
// Simulate Order always to be deleted
return true;
}
}
Code:
package order;
import lifecycle.Deletable;
public class OrderLine implements Deletable {
private Long id;
private String remarks;
private Order order;
OrderLine() {
}
public Long getId() {
return this.id;
}
public String getRemarks() {
return this.remarks;
}
public void setRemarks(final String someRemarks) {
this.remarks = someRemarks;
}
private Order getOrder() {
return this.order;
}
public void setOrder(final Order aOrder) {
this.order = aOrder;
}
public void handleDelete() {
if (this.isScheduledForDeletion()) {
System.out.println("DO delete -> " + this);
// Do nothing
} else {
System.out.println("DO NOT delete -> " + this);
// Disconnect from order
final Order order = this.getOrder();
order.getOrderLines().remove(this);
this.setOrder(null);
}
}
public String toString() {
return this.getClass().getName() + ": id = " + this.getId();
}
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o instanceof OrderLine) {
final OrderLine otherOrderLine = (OrderLine) o;
return this.getId().equals(otherOrderLine.getId());
}
return false;
}
public int hashCode() {
final String hashCodeString = this.getId() + this.getRemarks();
return hashCodeString.hashCode();
}
public boolean isScheduledForDeletion() {
// Simulate that OrderLine with id = 2 shouldn't be deleted
final Long spareId = new Long(2);
if (this.getId().equals(spareId)) {
return false;
}
return true;
}
}
Mapping documentsCode:
<?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="order.Order"
table="ORDERS"
lazy="false">
<id name="id" column="ORDER_ID" access="field">
<generator class="native"/>
</id>
<property name="remarks" column="ORDER_REMARKS"/>
<set name="orderLines"
table="ORDER_LINES"
inverse="true"
cascade="all"
lazy="false"
fetch="join">
<key column="ORDER_ID"/>
<one-to-many class="order.OrderLine"/>
</set>
</class>
</hibernate-mapping>
Code:
<?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="order.OrderLine"
table="ORDER_LINES"
lazy="false">
<id name="id" column="ORDER_LINE_ID" access="field">
<generator class="native"/>
</id>
<property name="remarks" column="ORDER_LINE_REMARKS"/>
<many-to-one name="order"
column="ORDER_ID"
lazy="false"
fetch="join"/>
</class>
</hibernate-mapping>
Hibernate ConfigurationCode:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
<property name="hibernate.connection.url">jdbc:hsqldb:test</property>
<property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">create</property>
<!-- How many objects around will be fetched -->
<!-- If you set max_fetch_depth to 0 and load an
account, only the account will be loaded.
If you set it to 1 and query on an account,
the account and its associated Product(s) will be
loaded. If you set it to 3, then trades will be
loaded as well on each product associated to the
account. -->
<property name="hibernate.max_fetch_depth">1</property>
<mapping resource="order/Order.hbm.xml"/>
<mapping resource="order/OrderLine.hbm.xml"/>
<listener type="delete" class="lifecycle.ControlledDeleteEventListener"/>
</session-factory>
</hibernate-configuration>
DeleteEventListenerCode:
package lifecycle;
import org.hibernate.HibernateException;
import org.hibernate.event.DeleteEvent;
import org.hibernate.event.def.DefaultDeleteEventListener;
public class ControlledDeleteEventListener extends DefaultDeleteEventListener {
/**
*
*/
private static final long serialVersionUID = 1L;
public void onDelete(final DeleteEvent aDeleteEvent)
throws HibernateException {
final Object obj = aDeleteEvent.getObject();
if (obj instanceof Deletable) {
final Deletable deletableObject = (Deletable) obj;
// Determine if we should delete the object or not
if (deletableObject.isScheduledForDeletion()) {
System.out.println("You're about to be wasted! ["
+ deletableObject + "]");
deletableObject.handleDelete();
super.onDelete(aDeleteEvent);
} else {
System.out.println("You're spared! [" + deletableObject + "]");
deletableObject.handleDelete();
}
}
}
}
ClientCode:
package order;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class OrderClient {
private static SessionFactory sessionFactory;
static {
sessionFactory = new Configuration().configure().buildSessionFactory();
}
/**
* @param args
*/
public static void main(String[] args) {
final Order order = new Order();
order.setRemarks("Order No.1");
final OrderLine orderLine1 = new OrderLine();
orderLine1.setRemarks("OrderLine No.1");
order.addOrderLine(orderLine1);
final OrderLine orderLine2 = new OrderLine();
orderLine2.setRemarks("OrderLine No.2");
order.addOrderLine(orderLine2);
final Long orderId = OrderClient.saveOrder(order);
final Order loadedOrder = OrderClient.loadOrder(orderId);
System.out.println("Loaded order -> " + loadedOrder);
OrderClient.deleteOrder(loadedOrder);
OrderClient.queryAndPrint("from Order");
OrderClient.queryAndPrint("from OrderLine");
sessionFactory.close();
}
private static Long saveOrder(Order anOrder) {
final Session session = sessionFactory.openSession();
final Transaction transaction = session.beginTransaction();
final Long orderId = (Long) session.save(anOrder);
transaction.commit();
session.close();
return orderId;
}
private static Order loadOrder(final Long anOrderId) {
final Session session = sessionFactory.openSession();
final Transaction transaction = session.beginTransaction();
final Order loadedOrder = (Order) session.get(Order.class, anOrderId);
transaction.commit();
session.close();
return loadedOrder;
}
private static void deleteOrder(final Order anOrder) {
final Session session = sessionFactory.openSession();
final Transaction transaction = session.beginTransaction();
session.delete(anOrder);
transaction.commit();
session.close();
}
private static void queryAndPrint(final String aHqlQuery) {
System.out.println("-=# QUERY AND PRINT #=-");
System.out.println("HQL = '" + aHqlQuery + "'\n");
final Session session = sessionFactory.openSession();
final Transaction transaction = session.beginTransaction();
final Query query = session.createQuery(aHqlQuery);
final List resultList = query.list();
for (Object o : resultList) {
System.out.println(o);
}
transaction.commit();
session.close();
}
}
Very similar code did work when using Hibernate2 Lifecycle interfaces.
I'm very thankful for any help!
Kind regards, Andreas