OK, I've finally gotten around to making a little test case for this. I have two entities: Parent and Child.
Code:
@Entity
public class Parent implements Serializable {
private static final long serialVersionUID = 1L;
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
@OneToMany
private Set<Child> children;
public Parent() {
}
public Parent(String name) {
this.name = name;
this.children = new HashSet<Child>();
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void addChild(Child child) {
this.children.add(child);
}
public Set<Child> getChildren() {
return this.children;
}
}
Code:
@Entity
public class Child implements Serializable {
private static final long serialVersionUID = 1L;
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
public Child() {
}
public Child(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Now here is my test:
Code:
public class CascadeTest extends TestCase {
private Session session;
@Override
protected void setUp() throws Exception {
AnnotationConfiguration annotationConfiguration = new AnnotationConfiguration();
SessionFactory sessionFactory = annotationConfiguration
.addAnnotatedClass(Parent.class)
.addAnnotatedClass(Child.class)
.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect")
.setProperty("hibernate.connection.username", "username")
.setProperty("hibernate.connection.password", "password")
.setProperty("hibernate.connection.driver_class", "com.mysql.jdbc.Driver")
.setProperty("hibernate.connection.url", "jdbc:mysql://localhost/development_db")
.setProperty("hibernate.show_sql", "true")
.buildSessionFactory();
session = sessionFactory.openSession();
new SchemaExport(annotationConfiguration, session.connection()).create(true, true);
}
@Override
protected void tearDown() throws Exception {
session.close();
}
public void testCascade() throws Exception {
// create and save the child
Child child = new Child("child");
session.saveOrUpdate(child);
session.flush();
Long childId = child.getId();
assertNotNull(childId);
// create the parent, add the child and save the parent
Parent parent = new Parent("parent");
parent.addChild(child);
session.saveOrUpdate(parent);
session.flush();
assertNotNull(parent.getId());
// modify the child and save the parent
child.setName("new child name");
session.saveOrUpdate(parent);
session.flush();
session.clear();
// test that the child has not been modified
Child newChild = (Child) session.createCriteria(Child.class).add(Restrictions.eq("id", childId)).uniqueResult();
assertEquals("child", newChild.getName());
}
}
Note in the Parent class that the @OneToMany annotation is used with no cascade parameter. According to the Javadoc of that parameter, the default value for cascade is none.
As the test shows, the Child object's name is updated, the Parent is saved, then the Child is read back from the database. The test fails as the change to the Child's name has been persisted.
So here are my questions:
- Is this the expected behaviour?
- If it is the expected behaviour, is there any way to stop the Child object's change from being persisted when saving the Parent? Bear in mind that there is no such thing as CascadeType.NONE to pass to the @OneToMany annotation.
- If it is the expected behaviour, why does it go against what the javax.persistence.OneToMany annotation's javadoc says?