Hibernate忽略了ManyToMany连接列的可空性

时间:2013-11-11 20:19:15

标签: java hibernate orm many-to-many nullable

我有以下实体:

@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_idenemies_person_id列都 NOT NULLABLE ,因此它忽略了我的nullable=true设置。

有人可以帮我正确地注释我的实体并帮我解决这个问题吗?提前谢谢!

1 个答案:

答案 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);
    }
}