hibernate异常org.hibernate.exception.ConstraintViolationExceptio

HTTP Status 500 - Request processing failed; nested exception is
org.springframework.dao.DataIntegrityViolationException: could not
execute statement; SQL [n/a]; constraint [null]; nested exception is
org.hibernate.exception.ConstraintViolationException: could not
execute statement

在使用hibernate执行save操作时出现此异常,最后通过断点调试,发现这个save之前还有其他的save,并且使用了其他save的返回的id,来作为外键参照。
我们一般都是将service的一个方法放到一个事务中(查询操作当然不需要事务了)
伪代码如下:

@Transactional//生命式事务,基于aop实现
viod saveSomething(){

Object a=new Object();
Object b=new Object();
Session hiberSession=getSession();
hiberSession.save(a);
b.setAId(a.getId);//将a对象的主键作为外键参照,然而此时还没有提交事务,a对象中还没有id返回,也就是b对象引用了一个不存在的外键作为外键值
hiberSession.save(b);


}

正确操作如下(解决方法):

@Transactional//rolling back on runtime exceptions
viod saveSomething(){

Object a=new Object();
Object b=new Object();
Session hiberSession=getSession();
hiberSession.save(a);
hiberSession.flush();//刷新缓存,将sql执行,这个默认是在事务提交前自动执行的
b.setAId(a.getId);//将a对象的主键作为外键参照
hiberSession.save(b);


}

这里需要注意:session的flush与事务提交的区别:
代码:
先看看session的flush,最好的说明无疑是看源码了:

session的flush与事务commit(只是简单理解)

session.flush

/**
	 * Force this session to flush. Must be called at the end of a
	 * unit of work, before committing the transaction and closing the
	 * session (depending on {@link #setFlushMode(FlushMode)},
	 * {@link Transaction#commit()} calls this method).
	 * <p/>
	 * <i>Flushing</i> is the process of synchronizing the underlying persistent
	 * store with persistable state held in memory.
	 *
	 * @throws HibernateException Indicates problems flushing the session or
	 * talking to the database.
	 */
	public void flush() throws HibernateException;

务必是在某个单元(一个整块)的任务末尾,事物提交和session关闭之前 刷新session

  • Tracnsaction的commit方法会自动调用session的flush操作
  • flush操作是同步底层持久化的过程,以存储在内存中的可持久化状态进行存储(注意哦,只是存在内存,也就是缓存中,并且有个状态标记这条缓存是可以持久化的,还没存到磁盘(数据库中))

transaction.commit

/**
	 * Commit this transaction.  This might entail a number of things depending on the context:<ul>
	 *     <li>
	 *         If this transaction is the {@link #isInitiator initiator}, {@link Session#flush} the {@link Session}
	 *         with which it is associated (unless {@link Session} is in {@link FlushMode#MANUAL}).
	 *     </li>
	 *     <li>
	 *         If this transaction is the {@link #isInitiator initiator}, commit the underlying transaction.
	 *     </li>
	 *     <li>
	 *         Coordinate various callbacks
	 *     </li>
	 * </ul>
	 *
	 * @throws HibernateException Indicates a problem committing the transaction.
	 */

问题:

既然事务的commit会自动执行flush,那么为什么不手动flush就会导致a对象的主键没返回呢???

猜测:

博主暂时猜测:自动的flush操作可能会打乱正常得逻辑的sql执行顺序,如果是手动flush就能保证a的sql在b的sql之前执行,
如此猜测的起点是:java虚拟机为了优化可以对代码进行重排序,这点大家都是知道的,那么hibernate这里的事务有没有可能是这样的呢?进行某种程度上的优化,来改变sql的执行顺序,我的pojo里面虽然有外键字段但是是没有指定参照于某个表的,只是让他作为普通的字段,数据库里面是建立了外键约束的。(以上暂时只是猜测,后续研究再来更正,各位博友如果知道原由,还望留言给出,谢谢!)

博主当时是没注意这个外键关联的问题,自己单独测试没有出现此异常,当时将系统投入使用时,发现出现此异常。当时以为是网络原因,当时生产服务器配置很高,这点排除;除此之外这个异常看名字也是和网络没半毛钱关系,再仔细看异常名字,包含volatile字样,这应该是和原子操作或者是事务的操作有点关系了。
找到该异常定义处:


## DataIntegrityViolationException.java

/**
 * Exception thrown when an attempt to insert or update data
 * results in violation of an integrity constraint. Note that this
 * is not purely a relational concept; unique primary keys are
 * required by most database types.
 *
 * @author Rod Johnson
 * */
 public class DataIntegrityViolationException extends NonTransientDataAccessException
 

大意是说当尝试insert或者update数据时导致违反完整性约束。
什么是完整性约束呢?

完整性约束

完整性约束简单来说就是为了保证只允许正确的数据存入到数据库。
什么是正确的数据?
不违反数据库里面定义的约束,比如非空约束(不允许该字段为null),主键约束(唯一且不空),外键约束(非null)等等
而博主的问题显然是由于外键约束问题导致的。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值