Java 如何解决Hibernate 3';更新持久实体时的ConstraintViolationException';什么是收藏品?

Java 如何解决Hibernate 3';更新持久实体时的ConstraintViolationException';什么是收藏品?,java,hibernate,Java,Hibernate,我试图从Hibernate3的角度来发现为什么两个几乎相同的类集的行为不同。一般来说,我对Hibernate还比较陌生,我希望我错过了一些关于映射或计时问题或类似的东西,但我昨天花了一整天的时间盯着这两个集合以及任何可能导致一个能够持久化而另一个不能完全逃脱我的差异 对于这个问题的长度,我提前表示歉意,但这一切都取决于一些非常具体的实现细节 我用注释映射了下面的类,并由Hibernate3管理。?(如果具体的版本被证明是相关的,我会找出它是什么)。Java版本是1.6 ... @Embedda

我试图从Hibernate3的角度来发现为什么两个几乎相同的类集的行为不同。一般来说,我对Hibernate还比较陌生,我希望我错过了一些关于映射或计时问题或类似的东西,但我昨天花了一整天的时间盯着这两个集合以及任何可能导致一个能够持久化而另一个不能完全逃脱我的差异

对于这个问题的长度,我提前表示歉意,但这一切都取决于一些非常具体的实现细节

我用注释映射了下面的类,并由Hibernate3管理。?(如果具体的版本被证明是相关的,我会找出它是什么)。Java版本是1.6

...

@Embeddable
public class JobStateChange implements Comparable<JobStateChange> {

    @Temporal(TemporalType.TIMESTAMP)
    @Column(nullable = false)
    private Date date;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = JobState.FIELD_LENGTH)
    private JobState state;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "acting_user_id", nullable = false)
    private User actingUser;

    public JobStateChange() {

    }

    @Override
    public int compareTo(final JobStateChange o) {
        return this.date.compareTo(o.date);
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        } else if (!(obj instanceof JobStateChange)) {
            return false;
        }

        JobStateChange candidate = (JobStateChange) obj;

        return this.state == candidate.state
            && this.actingUser.equals(candidate.getUser())
            && this.date.equals(candidate.getDate());
    }

    @Override
    public int hashCode() {
        return this.state.hashCode()
             + this.actingUser.hashCode()
             + this.date.hashCode();
    }

}
在运行测试之前,使用以下数据对数据库进行种子设定

...

insert into job (id, agency, payment_type, payment_file, payment_control_number, date_of_payment, beginningCheckNumber, endingCheckNumber, item_count, current_state, printer_id, wrapping_system_type, truck_number)
values (-3, 'RRB', 'Monthly', 'Monthly','4501','1998-12-01 08:31:16' , '00000001','00040000', 40000, 'UNASSIGNED', null, 'KERN', '02');

insert into job_state (job_id, acting_user_id, date, state)
values (-3, -1, '1998-11-30 08:31:17', 'UNASSIGNED');

...
数据库模式由Hibernate工具自动生成和重建后

在调用Session.flush()之前,以下测试可以正常运行

引发的错误是SQLCODE=-803,SQLSTATE=23505:

could not insert collection rows: [jaci.model.job.Job.stateChanges#-3]

org.hibernate.exception.ConstraintViolationException: could not insert collection rows: [jaci.model.job.Job.stateChanges#-3]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:94)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.persister.collection.AbstractCollectionPersister.insertRows(AbstractCollectionPersister.java:1416)
at org.hibernate.action.CollectionUpdateAction.execute(CollectionUpdateAction.java:86)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:170)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
at jaci.dao.JobDaoIntegrationTest.saveJob_JobAdvancedToAssigned_AllExpectedStateChanges(JobDaoIntegrationTest.java:98)
at org.springframework.test.context.junit4.SpringTestMethod.invoke(SpringTestMethod.java:160)
at org.springframework.test.context.junit4.SpringMethodRoadie.runTestMethod(SpringMethodRoadie.java:233)
at org.springframework.test.context.junit4.SpringMethodRoadie$RunBeforesThenTestThenAfters.run(SpringMethodRoadie.java:333)
at org.springframework.test.context.junit4.SpringMethodRoadie.runWithRepetitions(SpringMethodRoadie.java:217)
at org.springframework.test.context.junit4.SpringMethodRoadie.runTest(SpringMethodRoadie.java:197)
at org.springframework.test.context.junit4.SpringMethodRoadie.run(SpringMethodRoadie.java:143)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:160)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:97)
Caused by: com.ibm.db2.jcc.b.lm: DB2 SQL Error: SQLCODE=-803, SQLSTATE=23505, SQLERRMC=1;ACI_APP.JOB_STATE, DRIVER=3.50.152
at com.ibm.db2.jcc.b.wc.a(wc.java:575)
at com.ibm.db2.jcc.b.wc.a(wc.java:57)
at com.ibm.db2.jcc.b.wc.a(wc.java:126)
at com.ibm.db2.jcc.b.tk.b(tk.java:1593)
at com.ibm.db2.jcc.b.tk.c(tk.java:1576)
at com.ibm.db2.jcc.t4.db.k(db.java:353)
at com.ibm.db2.jcc.t4.db.a(db.java:59)
at com.ibm.db2.jcc.t4.t.a(t.java:50)
at com.ibm.db2.jcc.t4.tb.b(tb.java:200)
at com.ibm.db2.jcc.b.uk.Gb(uk.java:2355)
at com.ibm.db2.jcc.b.uk.e(uk.java:3129)
at com.ibm.db2.jcc.b.uk.zb(uk.java:568)
at com.ibm.db2.jcc.b.uk.executeUpdate(uk.java:551)
at org.hibernate.jdbc.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:46)
at org.hibernate.persister.collection.AbstractCollectionPersister.insertRows(AbstractCollectionPersister.java:1389)
这就是我的问题所在……一个几乎完全相同的类集(事实上,完全相同,以至于我一直在想方设法使它成为一个为两个业务实体服务的单一类)运行得非常好。除了名称外,它是相同的。这不是工作,而是网络。不是JobStateChange,而是WebStateChange。它不是JobState,而是WebState。作业和Web的状态更改分类集都映射为Hibernate CollectionFements。两者都是@embedded。两者都是SortType.Natural。两者都由一个枚举支持,枚举中包含一些高级规则。然而,当对Web运行几乎相同的测试时,不会发现任何问题,数据也会很好地刷新。为了简洁起见,我不会在这里包括所有的Web类,但我会包括测试,如果有人想查看实际的源代码,我会包括它们(只需留下评论)

数据种子:

insert into web (id, stock_type, pallet, pallet_id, date_received, first_icn, last_icn, shipment_id, current_state)
values (-1, 'PF', '0011', 'A', '2008-12-31 08:30:02', '000000001', '000080000', -1, 'UNSTAGED');

insert into web_state (web_id, date, state, acting_user_id)
values (-1, '2008-12-31 08:30:03', 'UNSTAGED', -1);
测试:

...

@ContextConfiguration(locations = { "/applicationContext-data.xml", "/applicationContext-service.xml" })
public class WebDaoIntegrationTest
extends AbstractTransactionalJUnit4SpringContextTests {

    @Autowired
    private WebDao webDao;

    @Autowired
    private UserService userService;

    @Autowired
    private SessionFactory sessionFactory;

...

    @Test
    public void saveWeb_WebAdvancedToNewState_AllExpectedStateChanges() {
        Web web = this.webDao.getWeb(-1L);
        Date advancedToUnstaged = new GregorianCalendar(2008, 11, 31, 8, 30, 3).getTime();

        assertEquals(WebState.UNSTAGED, web.getCurrentState());
        assertEquals(advancedToUnstaged, web.getState(WebState.UNSTAGED).getDate());

        Date advancedToStaged = new Date();
        web.advanceState(
                this.userService.getUserByUsername("admin"),
                advancedToStaged);

        this.sessionFactory.getCurrentSession().flush();

        web = this.webDao.getWeb(web.getId());

        assertEquals(
                "Web should have moved to STAGED State.",
                WebState.STAGED,
                web.getCurrentState());
        assertEquals(advancedToUnstaged, web.getState(WebState.UNSTAGED).getDate());
        assertEquals(advancedToStaged, web.getState(WebState.STAGED).getDate());

        assertNotNull(web.getState(WebState.UNSTAGED));
        assertNotNull(web.getState(WebState.STAGED));
    }

...

}
如您所见,我断言Web是按照我预期的方式重新构建的,我将其推进,将其刷新到DB,然后重新获取并验证状态是否如我预期的那样。一切都很完美。乔布斯却不是这样

一个可能相关的细节:如果我停止将JobStateChange.data映射为时间戳而不是日期,并确保所有状态更改总是在不同的日期发生,则重构代码可以正常工作。问题在于,这个特定的业务实体可以在一天内经历许多状态变化,因此需要按时间戳而不是日期进行排序。如果不这样做,则无法对状态更改进行正确排序。这就是说,WebStateChange.date也被映射为时间戳,所以我仍然完全不知道这个错误是从哪里产生的

我试图做一个相当彻底的工作,给出实现的所有技术细节,但由于这个问题非常具体,如果我遗漏了什么,请在评论中告诉我,我会将其包括在内

非常感谢你的帮助

更新:由于它对问题的解决非常重要,因此我还必须包含WebStateChange类的相关部分

...

@Embeddable
public class WebStateChange implements Comparable<WebStateChange> {

    @Temporal(TemporalType.TIMESTAMP)
    @Column(nullable = false)
    private Date date;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = WebState.FIELD_LENGTH)
    private WebState state;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "acting_user_id", nullable = false)
    private User actingUser;

...

    WebStateChange(
            final WebState state,
            final User actingUser,
            final Date date) {
        ExceptionUtils.illegalNullArgs(state, actingUser, date);
        this.state = state;
        this.actingUser = actingUser;
        this.date = new Date(date.getTime());
    }

    @Override
    public int compareTo(final WebStateChange otherStateChange) {
        return this.date.compareTo(otherStateChange.date);
    }

    @Override
    public boolean equals(final Object candidate) {
        if (this == candidate) {
            return true;
        } else if (!(candidate instanceof WebStateChange)) {
            return false;
        }

        WebStateChange candidateWebState = (WebStateChange) candidate;

        return this.getState() == candidateWebState.getState()
            && this.getUser().equals(candidateWebState.getUser())
            && this.getDate().equals(candidateWebState.getDate());
    }

    @Override
    public int hashCode() {
        return this.getState().hashCode()
             + this.getUser().hashCode()
             + this.getDate().hashCode();
    }

...

}
。。。
@可嵌入
公共类WebStateChange实现了可比较的{
@时态(TemporalType.TIMESTAMP)
@列(nullable=false)
私人日期;
@枚举(EnumType.STRING)
@列(nullable=false,length=WebState.FIELD\u length)
私有国家;
@manytone(fetch=FetchType.LAZY)
@JoinColumn(name=“acting\u user\u id”,null=false)
私人用户;
...
韦氏状态变化(
最终网站状态,
最终用户actingUser,
最后日期){
例外情况。非法无担保人(州、州、日期);
this.state=状态;
this.actingUser=actingUser;
this.date=新日期(date.getTime());
}
@凌驾
公共int比较(最终WebStateChange otherStateChange){
将此.date.compareTo返回(otherStateChange.date);
}
@凌驾
公共布尔等于(最终候选对象){
if(this==候选者){
返回true;
}else if(!(WebStateChange的候选实例)){
返回false;
}
WebStateChange候选WebState=(WebStateChange)候选;
返回此.getState()==candidateWebState.getState()
&&this.getUser()等于(candidateWebState.getUser())
&&this.getDate()等于(candidateWebState.getDate());
}
@凌驾
公共int hashCode(){
返回此.getState().hashCode()
+this.getUser().hashCode()
+this.getDate().hashCode();
}
...
}

JobStateChange中的equals方法使用直接字段访问。将其更改为对各种属性使用getter将解决此问题。您也可以考虑使用<代码> HiBeNeTeApple帮助器.GETCaseOutOutExalPrimeIGrimeServer < /Case>方法执行<代码>实例< <代码> >比较> < /P> 例如,
JobStateChange.equals
方法可能如下所示:

@Override
public boolean equals(final Object obj) {
    if (this == obj) {
        return true;
    } else if (!(HibernateProxyHelper.getClassWithoutInitializingProxy(obj) 
                 instanceof JobStateChange)) {
        return false;
    }

    JobStateChange candidate = (JobStateChange) obj;

    return this.getState() == candidate.getState()
        && this.getActingUser().equals(candidate.getUser())
        && this.getDate().equals(candidate.getDate());
}
同样地,
JobStateChange.hashCode()
方法也应该使用getter(我还建议编写
hashCode
方法来匹配Joshua Bloch在中建议的算法(从第38页开始),但这与问题并不相关):

Hibernate背后的部分“魔力”是动态代理。在许多情况下,Hibernate会(在运行时)创建实体类的子类,并覆盖持久化属性的getter和setter方法。因此,您不能直接在
equals
hashCode
中引用属性,而是应该使用属性getter和sett访问它们
...

@ContextConfiguration(locations = { "/applicationContext-data.xml", "/applicationContext-service.xml" })
public class WebDaoIntegrationTest
extends AbstractTransactionalJUnit4SpringContextTests {

    @Autowired
    private WebDao webDao;

    @Autowired
    private UserService userService;

    @Autowired
    private SessionFactory sessionFactory;

...

    @Test
    public void saveWeb_WebAdvancedToNewState_AllExpectedStateChanges() {
        Web web = this.webDao.getWeb(-1L);
        Date advancedToUnstaged = new GregorianCalendar(2008, 11, 31, 8, 30, 3).getTime();

        assertEquals(WebState.UNSTAGED, web.getCurrentState());
        assertEquals(advancedToUnstaged, web.getState(WebState.UNSTAGED).getDate());

        Date advancedToStaged = new Date();
        web.advanceState(
                this.userService.getUserByUsername("admin"),
                advancedToStaged);

        this.sessionFactory.getCurrentSession().flush();

        web = this.webDao.getWeb(web.getId());

        assertEquals(
                "Web should have moved to STAGED State.",
                WebState.STAGED,
                web.getCurrentState());
        assertEquals(advancedToUnstaged, web.getState(WebState.UNSTAGED).getDate());
        assertEquals(advancedToStaged, web.getState(WebState.STAGED).getDate());

        assertNotNull(web.getState(WebState.UNSTAGED));
        assertNotNull(web.getState(WebState.STAGED));
    }

...

}
...

@Embeddable
public class WebStateChange implements Comparable<WebStateChange> {

    @Temporal(TemporalType.TIMESTAMP)
    @Column(nullable = false)
    private Date date;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = WebState.FIELD_LENGTH)
    private WebState state;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "acting_user_id", nullable = false)
    private User actingUser;

...

    WebStateChange(
            final WebState state,
            final User actingUser,
            final Date date) {
        ExceptionUtils.illegalNullArgs(state, actingUser, date);
        this.state = state;
        this.actingUser = actingUser;
        this.date = new Date(date.getTime());
    }

    @Override
    public int compareTo(final WebStateChange otherStateChange) {
        return this.date.compareTo(otherStateChange.date);
    }

    @Override
    public boolean equals(final Object candidate) {
        if (this == candidate) {
            return true;
        } else if (!(candidate instanceof WebStateChange)) {
            return false;
        }

        WebStateChange candidateWebState = (WebStateChange) candidate;

        return this.getState() == candidateWebState.getState()
            && this.getUser().equals(candidateWebState.getUser())
            && this.getDate().equals(candidateWebState.getDate());
    }

    @Override
    public int hashCode() {
        return this.getState().hashCode()
             + this.getUser().hashCode()
             + this.getDate().hashCode();
    }

...

}
@Override
public boolean equals(final Object obj) {
    if (this == obj) {
        return true;
    } else if (!(HibernateProxyHelper.getClassWithoutInitializingProxy(obj) 
                 instanceof JobStateChange)) {
        return false;
    }

    JobStateChange candidate = (JobStateChange) obj;

    return this.getState() == candidate.getState()
        && this.getActingUser().equals(candidate.getUser())
        && this.getDate().equals(candidate.getDate());
}
@Override
public int hashCode() {
    return this.getState().hashCode()
         + this.getActingUser().hashCode()
         + this.getDate().hashCode();
}