The ManyToOne cascading does not appear to be working as I expect it to.
When I set the following cascade on a ManyToOne
@ManyToOne (cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH} )
I expect the saveOrUpdate call to cascade on all operations except delete if the "One" object (a CreditUnion object) is transient. When I set cascade type to ALL, the CreditUnion object is successfully saved with the cascade, otherwise I receive the TransientObject Exception Is this a bug?
I have included all of my files below for your review.
Object creation
Code:
//we Create a Credit Union and a ProcessInformation object to test
//cascading
CreditUnion cu = new CreditUnion();
cu.setName("My Test CU");
ProcessInformation info = new ProcessInformation();
info.setNumberOfAccounts(new Long(20));
info.setCreditUnion(cu);
cu.addToProcessorRun(info);
dao.saveOrUpdate(info);
Code to saveCode:
/*
* (non-Javadoc)
*
* @see com.purdueefcu.statements.dataaccess.IdentityDao#saveOrUpdate(com.purdueefcu.statements.dataaccess.bean.Identity)
*/
public void saveOrUpdate(Identity identity) {
if (identity == null) {
throw new NullPointerException("identity cannot be null");
}
this.getHibernateTemplate().saveOrUpdate(identity);
}
Hibernate version: hibernate 3.3.0, annotations 3.3.0
Mapping documents:Identity
Code:
@MappedSuperclass
//TODO TN, can we do table per subclass?
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Identity {
/**
* The id to map to the identity column of the table No setter is used to
* avoid programmer error. Only the database should set the id.
*
* @uml.property name="id"
*/
private Long id = null;
/**
* Getter of the property <tt>id</tt>
*
* @return Returns the id.
* @uml.property name="id"
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
// override and use direct field access to avoid creating a setter since
// only hibernate should set the id from the id generated by the db
@AccessType("field")
public Long getId() {
return id;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + ((id == null) ? 0 : id.hashCode());
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Identity other = (Identity) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
Auditable
Code:
@MappedSuperclass
public abstract class Auditable extends Identity {
/**
* The date this object was created
*
* @uml.property name="createDate"
*/
private Date createDate;
/**
* The update timestamp
*
* @uml.property name="updateDate"
*/
private Date updateDate;
public Auditable() {
super();
}
/**
* Getter of the property <tt>createDate</tt>
*
* @return Returns the createDate.
* @uml.property name="createDate"
*/
@Basic
public Date getCreateDate() {
return createDate;
}
/**
* Setter of the property <tt>createDate</tt>
*
* @param createDate
* The createDate to set.
* @uml.property name="createDate"
*/
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
/**
* Getter of the property <tt>updateDate</tt>
*
* @return Returns the updateDate.
* @uml.property name="updateDate"
*/
@Basic
public Date getUpdateDate() {
return updateDate;
}
/**
* Setter of the property <tt>updateDate</tt>
*
* @param updateDate
* The updateDate to set.
* @uml.property name="updateDate"
*/
public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}
CreditUnion
Code:
@Entity
public class CreditUnion extends Auditable {
/**
* @uml.property name="name2"
*/
private String name;
private List<ProcessInformation> processorRuns;
/**
*
*/
public CreditUnion() {
super();
}
/**
* @return the name
* @uml.property name="name"
*/
@Basic
public String getName() {
return name;
}
/**
* @param name
* the name to set
* @uml.property name="name"
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the processorRuns
*/
//links save/update/delete to all accounts. The pointer to our current object in
//the ProcessInformation object is the processInformation property
@OneToMany(mappedBy="creditUnion", cascade = {CascadeType.ALL})
public List<ProcessInformation> getProcessorRuns() {
return processorRuns;
}
/**
* @param processorRuns
* the processorRuns to set
*/
public void setProcessorRuns(List<ProcessInformation> processorRuns) {
this.processorRuns = processorRuns;
}
/**
* Add the run to the list of runs
*
* @param run
*/
public void addToProcessorRun(ProcessInformation run) {
if (this.processorRuns == null) {
this.processorRuns = new ArrayList<ProcessInformation>();
}
this.processorRuns.add(run);
}
}
ProcessInformation
Code:
@Entity
public class ProcessInformation extends Auditable {
private CreditUnion creditUnion;
//number of accounts queued in the run
private Long numberOfAccounts;
private List<Account> accounts;
/**
*
*/
public ProcessInformation() {
super();
}
/**
* @return the creditUnion
*/
@ManyToOne (cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH} )
public CreditUnion getCreditUnion() {
return creditUnion;
}
/**
* @param creditUnion the creditUnion to set
*/
public void setCreditUnion(CreditUnion creditUnion) {
this.creditUnion = creditUnion;
}
/**
* @return the numberOfAccounts
*/
@Basic
public Long getNumberOfAccounts() {
return numberOfAccounts;
}
/**
* @param numberOfAccounts the numberOfAccounts to set
*/
public void setNumberOfAccounts(Long numberOfAccounts) {
this.numberOfAccounts = numberOfAccounts;
}
/**
* @return the accounts
*/
//links save/update/delete to all accounts. The pointer to our current object in
//the Account object is the processInformation property
@OneToMany (mappedBy="processInformation", cascade = {CascadeType.ALL})
public List<Account> getAccounts() {
return accounts;
}
/**
* @param accounts the accounts to set
*/
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
/**
* Add the account to the list
* @param acct
*/
public void addAccount(Account acct){
if(accounts == null){
this.accounts = new ArrayList<Account>();
}
this.accounts.add(acct);
}
}
Name and version of the database you are using:
MySQL 5.0 InnoDB Dialect