This problem sounds similiar to
http://forum.hibernate.org/viewtopic.php?t=983185
but I'm creating a separate post in case its not.
I have a legacy database with the a parent/child relationship where one of the children is the "default" child.
Code:
+--------+ 1..n +-------+
| Parent |/\____________| Child |
| |\/ | |
| |--------------| |
+--------+ 1 +-------+
create database testdb;
create table testdb.PARENT_TABLE (
PARENT_ID decimal(15,1) NOT NULL,
DEFAULT_CHILD_ID decimal(15,1) NOT NULL,
NAME varchar(20) default NULL
);
create table testdb.CHILD_TABLE (
CHILD_ID decimal(15,1) NOT NULL,
PARENT_ID decimal(15,1) NOT NULL,
NAME varchar(20) default NULL
);
To complicate matters I have my own PersistentIdentifierGenerator to generate ID the same way our legacy code does it. However, this doesn't appear to be the problem (at least not yet).
My problem has to do with the NOT NULL contraints in these relationships. My little test program creates a Parent and a Child, sets the relationships and persists them but when I commit the transaction, I get errors about
Column 'DEFAULT_CHILD_ID' cannot be null
When I inspect these two objects with a debugger before calling commit they look fine. The parent's default child is set to a non-null value. If I remove the NON NULL constraint from DEFAULT_CHILD_ID the records are written and DEFAULT_CHILD_ID is non-null value that does indeed reference the child record.
I've played around with @OneOnOne(mappedBy="CHILD_ID") in the parent and @ManyToOne(cascade=CascadeType.ALL) but in the child. This changes what errors I get and when I get them (during EntityManagerFactory creation instead of during the transaction commit). I can't seem to find in the write combination of settings to make my little test program work. Any suggestions will be greatly appreciated.
Hibernate version: 3.2.5 annotations 3.3.0 Mapping documents:Code between sessionFactory.openSession() and session.close():test program
Code:
package jpa.prototype.model;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.FlushModeType;
import javax.persistence.Persistence;
public class CreateData {
private static final EntityManagerFactory entityManagerFactory;
static {
entityManagerFactory = Persistence.createEntityManagerFactory("hpidm");
}
public static void main(String[] args) {
EntityManager entMgr = entityManagerFactory.createEntityManager();
entMgr.setFlushMode(FlushModeType.COMMIT); // just to be sure
EntityTransaction t = entMgr.getTransaction();
t.begin();
Parent p = new Parent();
p.setName("parent");
Child defaultChild = new Child();
p.setDefaultChild(defaultChild);
defaultChild.setParent(p);
defaultChild.setName("default Child");
entMgr.persist(defaultChild);
entMgr.persist(p);
t.commit();
entMgr.close();
entityManagerFactory.close();
}
}
Parent pojo
Code:
package jpa.prototype.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.hibernate.annotations.GenericGenerator;
@Entity
@GenericGenerator(name="LegacyIdGenerator",
strategy="jpa.prototype.model.LegacyIdGenerator")
@Table(name="PARENT_TABLE", catalog="testdb", uniqueConstraints = @UniqueConstraint(columnNames = "NAME"))
public class Parent {
private Long id;
private Child defaultChild;
private String name;
public Parent() { /* do nothing */ }
public Parent(Long aId, Child aDefaultChild, String aName) {
id = aId;
defaultChild = aDefaultChild;
name = aName;
}
@Id
@GeneratedValue(generator = "LegacyIdGenerator")
@Column(name = "PARENT_ID", unique = true, nullable = false)
public Long getId() { return id; }
public void setId(Long aId) { id = aId; }
@OneToOne/* (mappedBy="CHILD_ID", optional=false) */
@JoinColumn(name = "DEFAULT_CHILD_ID")
public Child getDefaultChild() { return defaultChild; }
public void setDefaultChild(Child aDfltChild) {
aDfltChild.setParent(this);
defaultChild = aDfltChild;
}
@Column(name = "NAME")
public String getName() { return name; }
public void setName(String aName) { name = aName; }
}
Child pojo
Code:
package jpa.prototype.model;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.hibernate.annotations.GenericGenerator;
@Entity
@GenericGenerator(name="LegacyIdGenerator",
strategy="jpa.prototype.model.LegacyIdGenerator")
@Table(name="CHILD_TABLE", catalog="testdb", uniqueConstraints = @UniqueConstraint(columnNames = "NAME"))
public class Child {
private Long id;
private Parent parent;
private String name;
public Child() { /* do nothing */ }
public Child(Long aId, Parent aParent, String aName) {
id = aId;
parent = aParent;
name = aName;
}
@Id
@GeneratedValue(generator="LegacyIdGenerator")
@Column(name = "CHILD_ID", unique = true, nullable = false)
public Long getId() {
return id;
}
void setId(Long aId) {
id = aId;
}
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name = "PARENT_ID")
public Parent getParent() {
return parent;
}
public void setParent(Parent aParent) {
parent = aParent;
}
@Column(name = "NAME")
public String getName() {
return name;
}
public void setName(String aName) {
name = aName;
}
}
Simplified legacy Identifier generator
Code:
package jpa.prototype.model;
import java.io.Serializable;
import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.id.PersistentIdentifierGenerator;
// Simplified version of legacy ID generator.
public class LegacyIdGenerator implements PersistentIdentifierGenerator {
private static long seed = System.currentTimeMillis();
private static synchronized long generateId() {
return ++seed;
}
public Object generatorKey() {
return generateId();
}
public String[] sqlCreateStrings(Dialect aDialect)
throws HibernateException {
return null;
}
public String[] sqlDropStrings(Dialect aDialect) throws HibernateException {
return null;
}
public Serializable generate(SessionImplementor aSession, Object aObject)
throws HibernateException {
return generateId();
}
}
Full stack trace of any exception that occurs:
Name and version of the database you are using: MySQL Serve 5.0
The generated SQL (show_sql=true):
Debug level Hibernate log excerpt:
Problems with Session and transaction handling?
Read this:
http://hibernate.org/42.html