So, let's go with a less complex example. Two classes, Person and Address:
Code:
package test.domain;
import java.util.Collection;
public class Person {
private long seq;
private String name;
private Collection addresses;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getSeq() {
return seq;
}
public void setSeq(long seq) {
this.seq = seq;
}
public Collection getAddresses() {
return addresses;
}
public void setAddresses(Collection addresses) {
this.addresses = addresses;
}
}
Code:
package test.domain;
public class Address {
private long seq;
private String description;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public long getSeq() {
return seq;
}
public void setSeq(long seq) {
this.seq = seq;
}
}
The mapping files:
Person.hbm.xml
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="test.domain.Person" table="PERSON">
<id name="seq" column="seq" type="long">
<generator class="sequence">
<param name="sequence">TSTQ_PER</param>
</generator>
</id>
<property name="name" column="NAME"/>
<set name="addresses" cascade="all-delete-orphan">
<key column="PER_SEQ" not-null="true"/>
<one-to-many class="test.domain.Address"/>
</set>
</class>
</hibernate-mapping>
Address.hbm.xml
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="test.domain.Address" table="ADDRESS">
<id name="seq" column="seq" type="long">
<generator class="sequence">
<param name="sequence">TSTQ_ADD</param>
</generator>
</id>
<property name="description" column="DESCRIPTION"/>
</class>
</hibernate-mapping>
Two tables:
Code:
create table PERSON
(
SEQ NUMBER not null,
NAME VARCHAR2(200) not null
)
create table ADDRESS
(
SEQ NUMBER not null,
DESCRIPTION VARCHAR2(200) not null,
PER_SEQ NUMBER not null
)
The PersonDAO.java:
Code:
package test.dao;
import java.sql.Connection;
import java.util.List;
import org.apache.log4j.Logger;
import org.hibernate.FetchMode;
import org.hibernate.Hibernate;
import org.hibernate.JDBCException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.Restrictions;
import test.domain.Person;
import br.gov.rs.mp.util.HibernateUtil;
import br.gov.rs.mp.util.sql.SQLExceptionWrapper;
public class PersonDAO {
private final static Logger LOG = Logger.getLogger(PersonDAO.class.getName());
private static final SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
private Connection connection;
public PersonDAO(Connection connection){
this.connection = connection;
}
public Connection getConnection() {
return connection;
}
public List getList() {
List persons = null;
try {
Session session = sessionFactory.openSession(getConnection());
Query q = session.createQuery("from Person");
persons = q.list();
session.close();
} catch (Exception e) {
LOG.error("Erro inesperado.", e);
throw new RuntimeException("Erro inesperado.", e);
}
return persons;
}
public Person get(Person per){
Person person = null;
try {
Session session = sessionFactory.openSession(getConnection());
person = (Person)session.createCriteria(Person.class)
.setFetchMode("addresses", FetchMode.JOIN)
.add( Restrictions.idEq(new Long(per.getSeq())) )
.uniqueResult();
Hibernate.initialize(person.getAddresses());
session.close();
} catch (Exception e) {
LOG.error("Erro inesperado.", e);
throw new RuntimeException("Erro inesperado.", e);
}
return person;
}
public boolean delete(Person person) {
Session session = sessionFactory.openSession(getConnection());
Transaction trans = session.beginTransaction();
try {
person = this.get(person);
session.delete(person);
trans.commit();
} catch (JDBCException e) {
try {
trans.rollback();
} catch (Exception ex) {
LOG.error("Erro ao desfazer transação.", ex);
}
SQLExceptionWrapper wrapper = new SQLExceptionWrapper(e.getSQLException(), getConnection());
LOG.log(wrapper.getLoggerPriority(), wrapper.getMessage(), e);
throw new RuntimeException(wrapper.getMessage(), e);
} catch (Exception e) {
try {
trans.rollback();
} catch (Exception ex) {
LOG.error("Erro ao desfazer transação.", ex);
}
LOG.error("Erro inesperado.", e);
throw new RuntimeException("Erro inesperado.");
} finally {
if (session != null) {
try {
session.close();
} catch (Exception ignored){}
}
}
return true;
}
public boolean insert(Person person) {
Session session = sessionFactory.openSession(getConnection());
Transaction trans = session.beginTransaction();
try {
session.save(person);
trans.commit();
} catch (JDBCException e) {
try {
trans.rollback();
} catch (Exception ex) {
LOG.error("Erro ao desfazer transação.", ex);
}
SQLExceptionWrapper wrapper = new SQLExceptionWrapper(e.getSQLException(), getConnection());
LOG.log(wrapper.getLoggerPriority(), wrapper.getMessage(), e);
throw new RuntimeException(wrapper.getMessage(), e);
} catch (Exception e) {
try {
trans.rollback();
} catch (Exception ex) {
LOG.error("Erro ao desfazer transação.", ex);
}
LOG.error("Erro inesperado.", e);
throw new RuntimeException("Erro inesperado.");
} finally {
if (session != null) {
try {
session.close();
} catch (Exception ignored){}
}
}
return true;
}
public boolean update(Person person) {
Session session = sessionFactory.openSession(getConnection());
Transaction trans = session.beginTransaction();
try {
session.update(person);
trans.commit();
} catch (JDBCException e) {
try {
trans.rollback();
} catch (Exception ex) {
LOG.error("Erro ao desfazer transação.", ex);
}
SQLExceptionWrapper wrapper = new SQLExceptionWrapper(e.getSQLException(), getConnection());
LOG.log(wrapper.getLoggerPriority(), wrapper.getMessage(), e);
throw new RuntimeException(wrapper.getMessage(), e);
} catch (Exception e) {
try {
trans.rollback();
} catch (Exception ex) {
LOG.error("Erro ao desfazer transação.", ex);
}
LOG.error("Erro inesperado.", e);
throw new RuntimeException("Erro inesperado.");
} finally {
if (session != null) {
try {
session.close();
} catch (Exception ignored){}
}
}
return true;
}
}
And now the main method:
Code:
public static void main(String[] args) {
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection conn = DriverManager.getConnection(...);
PersonDAO dao = new PersonDAO(conn);
Person person = new Person();
person.setName("Me");
dao.insert(person);
person = dao.get(person);
Address addr = new Address();
person.getAddresses().add(addr);
System.out.println("address.seq=" + addr.getSeq());
try {
// Here, some error occurs, at database level:
// address.description can't be null
dao.update(person);
} catch (RuntimeException e) {
e.printStackTrace();
}
System.out.println("address.seq=" + addr.getSeq());
// now sets the description
addr.setDescription("New Address");
try {
// Now, no error occurs at database level, but errors occurs
// because hibernate tries to update an address that
// still does not exists in the database
dao.update(person);
} catch (RuntimeException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
The stacktrace shows an error caused by the first update (ORA-01400: cannot insert NULL into ("ADDRESS"."DESCRIPTION"),
followed by another error caused by the second update (Could not synchronize database state with session,
org.hibernate.StaleStateException: Unexpected row count: 0 expected: 1)
The two outs show different values for address.seq.
Please understand, my application
MUST handle database level errors, this is a big requirement for compatibility with other existent systems.
Thanks in advance