我们知道在处理hibernate的一对多的问题时,我们一般会在配置文件中写清它们的关系以及操作的规则。
比如,在做网页时,会有博主和文章表,是一对多的关系。我用hiberbnate来实现它们的级联关系,配置文件如下
博主Userinfo.hbm.xml代码如下:
<hibernate-mapping>
<!-- 指定类到表的映射 -->
<class name="test.pojo.Userinfo" table="userinfo">
<!--主键id的生成机制:native将自动根据数据库生成,mysql是自增长方式 -->
<id name="id" column="id">
<generator class="native"></generator>
</id>
<!--设定Userinfo类每个属性到userinfo表中对应列名的映射 -->
<property name="name" column="name" />
<property name="psw" column="psw"/>
<property name="signature" column="signature"/>
<property name="sex" column="sex"/>
<property name="age" column="age"/>
<property name="birthday" column="birthday"/>
<property name="qq" column="qq"/>
<property name="e_mail" column="e_mail"/>
<property name="phone" column="phone"/>
<property name="occupation" column="occupation"/>
<property name="score" column="score"/>
<property name="graduateSchool" column="graduateSchool"/>
<property name="country" column="country"/>
<property name="province" column="province"/>
<property name="head_path" column="head_path"/>
<!--save-update -->
<!-- 映射Userinfo对象与Article对象的一对对多关系,以article方式映射 ,按id顺序排序,并延迟加载-->
<bag name="articles" table="article" order-by="id asc" lazy="false" cascade="all-delete-orphan" >
<key column="userid"></key>
<one-to-many class="test.pojo.Article"></one-to-many>
</bag>
</class>
</hibernate-mapping>
其实关键的一段是
<bag name="articles" table="article" order-by="id asc" lazy="false" cascade="all-delete-orphan" >
<key column="userid"></key>
<one-to-many class="test.pojo.Article"></one-to-many>
</bag>
这一段实现对象与对象的级联关系
文章Article.hbm.xml代码如下:
<hibernate-mapping>
<!--指定类到表的映射 -->
<class name="test.pojo.Article" table="article">
<!--主键id的生成机制:native将自动根据数据生成,mysql是自增长方式 -->
<id name="id" column="id">
<generator class="native"></generator>
</id>
<!--设定Article 类每个属性到article表中 对应列名的映射 -->
<property name="title" column="title"></property>
<property name="content" column="content"></property>
<property name="publishTime" column="publishTime"></property>
<property name="published" column="published"></property>
<property name="clickCount" column="clickCount"></property>
<property name="type" column="type"></property>
<!--设定Article对象与Userinfo对象的多对一 关系 -->
<many-to-one name="user" class="test.pojo.Userinfo" fetch="select" cascade="delete" outer-join="true">
<column name="userid"/>
</many-to-one>
</class>
</hibernate-mapping>
关键的一段是
<many-to-one name="user" class="test.pojo.Userinfo" fetch="select" cascade="delete" outer-join="true">
<column name="userid"/>
</many-to-one>
这样写在测试时“增”,“改”,“查”是可以实现了,但是删除就不行。
现在,我要把一个博主删掉,代码:
Userinfo temuser=userDao.getUserinfoById(4);
userDao.deleteUser(temuser);
会报如下错误:
Hibernate:
select
userinfo0_.id as id0_0_,
userinfo0_.name as name0_0_,
userinfo0_.psw as psw0_0_,
userinfo0_.signature as signature0_0_,
userinfo0_.sex as sex0_0_,
userinfo0_.age as age0_0_,
userinfo0_.birthday as birthday0_0_,
userinfo0_.qq as qq0_0_,
userinfo0_.e_mail as e9_0_0_,
userinfo0_.phone as phone0_0_,
userinfo0_.occupation as occupation0_0_,
userinfo0_.score as score0_0_,
userinfo0_.graduateSchool as graduat13_0_0_,
userinfo0_.country as country0_0_,
userinfo0_.province as province0_0_,
userinfo0_.head_path as head16_0_0_
from
userinfo userinfo0_
where
userinfo0_.id=?
Hibernate:
select
articles0_.userid as userid0_1_,
articles0_.id as id1_,
articles0_.id as id1_0_,
articles0_.title as title1_0_,
articles0_.content as content1_0_,
articles0_.publishTime as publishT4_1_0_,
articles0_.published as published1_0_,
articles0_.clickCount as clickCount1_0_,
articles0_.type as type1_0_,
articles0_.userid as userid1_0_
from
article articles0_
where
articles0_.userid=?
order by
articles0_.id asc
查询用户‘xiang'成功!
Hibernate:
update
article
set
userid=null
where
userid=?
1953 [main] WARN org.hibernate.util.JDBCExceptionReporter - SQL Error: 0, SQLState: 22001
1953 [main] ERROR org.hibernate.util.JDBCExceptionReporter - Data truncation: Column was set to data type implicit default; NULL supplied for NOT NULL column 'userid' at row 1
1953 [main] ERROR org.hibernate.event.def.AbstractFlushingEventListener - Could not synchronize database state with session
org.hibernate.exception.DataException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:102)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:262)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:180)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1206)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:375)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
at test.DAO.UserinfoDAO.deleteUser(UserinfoDAO.java:70)
at test.test.TestUserinfo.main(TestUserinfo.java:25)
Caused by: java.sql.BatchUpdateException: Data truncation: Column was set to data type implicit default; NULL supplied for NOT NULL column 'userid' at row 1
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1213)
at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:912)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
... 9 more
这是为什么呢?
我们首先看它执行的sql语句,它为什么要update文章表呢?
其实这跟hibernate的inverse有很大的关系,现在来看下inverse:
下面是百度百科对inverse的解释
inverse的值有两种,“true”和“false”。inverse="false"是默认的值,如果设置为true 则表示对象的状态变化不会同步到数据库 ;设置成false则相反;
inverse的作用:在hibernate中是通过inverse的设置来决定是有谁来维护表和表之间的关系的。
我们说inverse设立不当会导致性能低下,其实是说inverse设立不当,会产生多余重复的SQL语句甚至致使JDBC exception的throw。这是我们在建立实体类关系时必须需要关注的地方。一般来说,inverse=true是推荐使用,双向关联中双方都设置 inverse=false的话,必会导致双方都重复更新同一个关系。但是如果双方都设立inverse=true的话,双方都不维护关系的更新,这也是 不行的,好在一对多中的一端:many-to-one默认是inverse=false,避免了这种错误的产生。但是多对多就没有这个默认设置了,所以很 多人经常在多对多的两端都使用inverse=true,结果导致连接表的数据根本没有记录,就是因为他们双分都没有责任维护关系。所以说,双向关联中最 好的设置是一端为inverse=true,一端为inverse=false。一般inverse=false会放在多的一端,那么有人提问了, many-to-many两边都是多的,inverse到底放在哪儿?其实hibernate建立多对多关系也是将他们分离成两个一对多关系,中间连接一个连接表。所以通用存在一对多的关系,也可以这样说:一对多是多对多的基本组成部分。
其中还有一段时cascade和inverse的区别
inverse只对set+one-to-many(或many-to-many)有效,对many-to-one, one-to-one无效。cascade对关系标记都有效。
inverse对集合对象整体起作用,cascade对集合对象中的一个一个元素起作用,如果集合为空,那么cascade不会引发关联操作。
而在binernate的文档里也写有
所以要是在Userinfo.hbm.xml没设置inverse的话,它默认的是false,那么它就会把级联关系刷新,因为user_id是在
article表中设置,所以会出现user_id=null,所以会报错。
当在Userinfo.hbm.xml设置 inverse="true",它就不会再去更新级联关系,就可以直接进入数据库层面操作数据,所以上面删除用户时同时也把有级联关系的文章删掉了。
所以解决级联的“增删查改”问题,应该在“一对多”关系中的“一方”设置inverse=true;
如:
<bag name="articles" table="article" order-by="id asc" lazy="false" cascade="all-delete-orphan" inverse="true">
<key column="userid"></key>
<one-to-many class="test.pojo.Article"></one-to-many>
</bag>
这样就行了。