我有以下实体:
@Entity
@AttributeOverrides({
@AttributeOverride(name="id", column=@Column(name="person_id))
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
@Table(name="people")
public class Person {
@Column(name="name")
private String name;
@ManyToMany(cascade=CascadeType.ALL)
@JoinColumn(name="friend_person_id", nullable=true)
private List<Person> friends;
@ManyToMany(cascade=CascadeType.ALL)
@JoinColumn(name="enemy_person_id", nullable=true)
private List<Person> enemies;
// constructor, getters/setters, etc.
}
简单地说,Person
有一个名字和一个朋友列表(也是所有人)以及一个敌人列表(更多人)。因此Person
与自身有2对多的关系:1为朋友,1为敌人。但是,一个人可能没有朋友和/或没有敌人。因此,我希望这些字段可以为空。
这是我的PersonDAO
,它公开了可用于将人员持久保存到数据库的公共方法:
// Inside PersonDAO
public void savePerson(Person person) {
Session session = getDaoUtils().newSessionFactory().openSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
// Execute the query.
session.saveOrUpdate(person);
transaction.commit();
} catch(Throwable throwable) {
transaction.rollback();
throw new RuntimeException(throwable);
} finally {
session.close();
}
}
最后,我有一个小测试脚本来测试这一切:
public class Tester {
public static void main(String[] args) {
Person tom = new Person("Tom", null, null); // Tom has no friends/enemies.
Person bob = new Person("Bob", null, null); // Bob also has no friends/enemies.
List<Person> friends = new ArrayList<Person>();
List<Person> enemies = new ArrayList<Person>();
friends.add(tom);
enemies.add(bob);
// Randy's friend is Tom. Randy's enemy is Bob.
Person randy = new Person("Randy", friends, enemies);
PersonDAO personDAO = new PersonDAO();
personDAO.savePerson(randy);
}
}
当我运行这个时,我得到以下异常(针对H2 DB):
INFO: HHH000010: On release of batch it still contained JDBC statements
Exception in thread "main" java.lang.RuntimeException: org.hibernate.exception.ConstraintViolationException: could not execute statement
at net.myuser.myapp.common.dal.dao.PersonDAO.savePerson(PersonDAO.java:38)
at net.myuser.myapp.tools.personloader.Tester.main(Tester.java:25)
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:129)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:136)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:58)
at org.hibernate.persister.collection.AbstractCollectionPersister.recreate(AbstractCollectionPersister.java:1256)
at org.hibernate.action.internal.CollectionRecreateAction.execute(CollectionRecreateAction.java:58)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:377)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:369)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:292)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:339)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:52)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1234)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:404)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
at net.myuser.myapp.common.dal.dao.PersonDAO.savePerson(PersonDAO.java:30)
... 1 more
Caused by: org.h2.jdbc.JdbcSQLException: NULL not allowed for column "FRIENDS_PERSON_ID"; SQL statement:
/* insert collection row net.myuser.myapp.common.dal.dto.Person.enemies */ insert into persons_persons (persons_person_id, enemies_word_id) values (?, ?) [23502-173]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:331)
at org.h2.message.DbException.get(DbException.java:171)
at org.h2.message.DbException.get(DbException.java:148)
at org.h2.table.Column.validateConvertUpdateSequence(Column.java:295)
at org.h2.table.Table.validateConvertUpdateSequence(Table.java:699)
at org.h2.command.dml.Insert.insertRows(Insert.java:123)
at org.h2.command.dml.Insert.update(Insert.java:86)
at org.h2.command.CommandContainer.update(CommandContainer.java:79)
at org.h2.command.Command.executeUpdate(Command.java:235)
at org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:154)
at org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:140)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:133)
... 14 more
所以看起来Hibernate试图将多对多的关系存储到一个表中?无论哪种方式,我都无法弄清楚它在做什么。运行此操作后,我在数据库中看到persons_persons
表,但其friends_person_id
和enemies_person_id
列都 NOT NULLABLE ,因此它忽略了我的nullable=true
设置。
有人可以帮我正确地注释我的实体并帮我解决这个问题吗?提前谢谢!
答案 0 :(得分:3)
@Entity
@Table(name = "people")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long Id;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "friends", joinColumns = @JoinColumn(name = "person_id"), inverseJoinColumns = @JoinColumn(name = "friend_id"))
private List<Person> friends;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "enemies", joinColumns = @JoinColumn(name = "person_id"), inverseJoinColumns = @JoinColumn(name = "enemy_id"))
private List<Person> enemies;
public Person() {
friends = new ArrayList<>();
enemies = new ArrayList<>();
}
/**
*
* Optionally force client classes through your add/remove methods if mutual
* relationship should be maintained.
*
* @return
*/
public List<Person> getFriends() {
return Collections.unmodifiableList(friends);
}
/**
* Optionally force client classes through your add/remove methods if mutual
* relationship should be maintained.
*
* @return
*/
public List<Person> getEnemies() {
return Collections.unmodifiableList(enemies);
}
/**
* Ensures mutual relationship set correctly.
*
* @param person
*/
public void addFriend(Person person) {
friends.add(person);
person.friends.add(this);
}
/**
* Ensures mutual relationship set correctly.
*
* @param person
*/
public void addEnemy(Person person) {
enemies.add(person);
person.enemies.add(this);
}
}