我们引用一个一对多的bag类型关联说明这个问题:
代码:
配置文件:
执行插入:
hibernate打印sql:
Hibernate: insert into ITEM (ITEM_NAME) values (?)
Hibernate: insert into BID (bidMoeny, ITEM_ID_M) values (?, ?)
Hibernate: update BID set ITEM_ID_M=? where BID_ID=?
那么你看到了多打印了一条sql,最后一条是荣誉的。
原因:
在操作两个实例之间的连接时,如果没有inverse属性,hibernate会试图执行两个不同的SQL语句,这两者更新
同一个外键列。通过指定inverse="true",显示地告诉hibernate连接的哪一端不应该与数据库同步。在这个例子
中,告诉hibernate他应该把在关联的Bid端所做的变化传播到数据库,忽略只对bids集合所做的变化。
如下:
执行相同插入:
hibernate打印sql:
Hibernate: insert into ITEM (ITEM_NAME) values (?)
Hibernate: insert into BID (bidMoeny, ITEM_ID_M) values (?, ?)
那么有些时候,是不能添加inverse="true"的,比如保存有序的list集合:
添加:insert="false" update="false"
但是这么做还是会打印三条sql,不添加则会报错
Repeated column in mapping for entity: cn.partner4java.hibernate.onetomany2.Bid column: ITEM_ID (should be mapped with insert="false" update="false")
如:
双向对多对时:
代码:
配置文件:
插入代码:
代码:
- publicclassItemimplementsSerializable {
- privateInteger itemId;
- privateString itemName;
- privateCollection<Bid> bids =newArrayList<Bid>();
- /**
- * 出价
- * @author partner4java
- *
- */
- publicclassBidimplementsSerializable {
- privateInteger bidId;
- privateDouble bidMoeny;
- privateItem item;
配置文件:
- <classname="Item"table="ITEM">
- <id name="itemId"column="ITEM_ID"type="integer">
- <generatorclass="native"/>
- </id>
- <property name="itemName"type="string"column="ITEM_NAME"/>
- lt;bag name="bids"cascade="save-update">
- <!-- 注意column="ITEM_ID_M"名称故意和Item主键不一致,这里是维护端生成的字段名称 -->
- <key column="ITEM_ID_M"></key>
- <one-to-manyclass="Bid"/>
- lt;/bag>
- </class>
- <classname="Bid"table="BID">
- <id name="bidId"column="BID_ID"type="integer">
- <generatorclass="native"/>
- </id>
- <property name="bidMoeny"type="double"></property>
- <!-- 注意column="ITEM_ID_M"名称故意和Item主键不一致,这里是说明一的一端的表,需要生成的对应关联字段是什么
- KEY `FK100DD8434F24F` (`ITEM_ID_M`),
- CONSTRAINT `FK100DD8434F24F` FOREIGN KEY (`ITEM_ID_M`) REFERENCES `item` (`ITEM_ID`) -->
- <many-to-one name="item"column="ITEM_ID_M"
- class="Item"not-null="true"></many-to-one>
- </class>
执行插入:
- Item item =newItem("hello");
- Bid bid =newBid(12D);
- item.getBids().add(bid);
- //不添加这个报错:not-null property references a null or transient value(因为添加了不能为空)
- //<name="bids" inverse="true" cascade="save-update">也决定了,维护端是Bid的item属性,所以要告诉Bid的item属性item的存在
- //要知道这个东西和cascade="save-update"并不想干,cascade只是负责是否级联保存的,和表之间的关系由谁维护无关
- bid.setItem(item);
- session.save(item);
hibernate打印sql:
Hibernate: insert into ITEM (ITEM_NAME) values (?)
Hibernate: insert into BID (bidMoeny, ITEM_ID_M) values (?, ?)
Hibernate: update BID set ITEM_ID_M=? where BID_ID=?
那么你看到了多打印了一条sql,最后一条是荣誉的。
原因:
在操作两个实例之间的连接时,如果没有inverse属性,hibernate会试图执行两个不同的SQL语句,这两者更新
同一个外键列。通过指定inverse="true",显示地告诉hibernate连接的哪一端不应该与数据库同步。在这个例子
中,告诉hibernate他应该把在关联的Bid端所做的变化传播到数据库,忽略只对bids集合所做的变化。
如下:
- <classname="Item"table="ITEM">
- <id name="itemId"column="ITEM_ID"type="integer">
- <generatorclass="native"/>
- </id>
- <property name="itemName"type="string"column="ITEM_NAME"/>
- lt;bag name="bids"inverse="true"cascade="save-update">
- <!-- 注意column="ITEM_ID_M"名称故意和Item主键不一致,这里是维护端生成的字段名称 -->
- <key column="ITEM_ID_M"></key>
- <one-to-manyclass="Bid"/>
- lt;/bag>
- </class>
执行相同插入:
hibernate打印sql:
Hibernate: insert into ITEM (ITEM_NAME) values (?)
Hibernate: insert into BID (bidMoeny, ITEM_ID_M) values (?, ?)
那么有些时候,是不能添加inverse="true"的,比如保存有序的list集合:
添加:insert="false" update="false"
但是这么做还是会打印三条sql,不添加则会报错
Repeated column in mapping for entity: cn.partner4java.hibernate.onetomany2.Bid column: ITEM_ID (should be mapped with insert="false" update="false")
如:
- <classname="Item"table="ITEM">
- <id name="itemId"column="ITEM_ID"type="integer">
- <generatorclass="native"/>
- </id>
- <property name="itemName"type="string"column="ITEM_NAME"/>
- <!-- 没有在集合中加入inverse="true,因为hibernate忽略反向集合的状态!
- 但是这一次,集合包含了正确的更新数据库所需要的信息:它的元素的位置。
- 如果只有每个Bid实例的状态被认为是同步的,集合又是方向的并且被忽略,
- 那么hibernate就没有值给BID_POSITOIN了-->
- <list name="bids"cascade="save-update">
- <key column="ITEM_ID"not-null="true"></key>
- <list-index column="BID_POSITOIN"></list-index>
- <one-to-manyclass="Bid"/>
- </list>
- </class>
- <classname="Bid"table="BID">
- <id name="bidId"column="BID_ID"type="integer">
- <generatorclass="native"/>
- </id>
- <property name="bidMoeny"type="double"></property>
- <!-- 如果通过被索引的集合映射双向的一对多实体关联(映射和数组也是这样),
- 就必须转换为方向段。无法被索引的集合变成inverse="true"。集合变成了
- 负责状态同步,并且一的端Bid必须变成反向。然而,多对一的映射没有
- inverse="true",因此需要在<many-to-one>中模拟这一模拟这一属性:
- 设置insert="false"update="false"
- 这俩个属性一起使用,实际上使属性变成了只读。关联的这一端因此被任何写操作忽略,
- 当内存状态与数据库同步时,集合的状态就是相关的状态。你已经转换了关联的方向/非方向端,
- 如果从set或者bag转换为list,这个是必要的条件。
- -->
- <many-to-one name="item"column="ITEM_ID_M"
- class="Item"not-null="true"
- insert="false"update="false"></many-to-one>
- </class>
双向对多对时:
代码:
- publicclassCategoryimplementsSerializable {
- privateInteger categoryId;
- privateString name;
- privateSet<Item> items =newHashSet<Item>();
- publicclassItemimplementsSerializable {
- privateInteger itemId;
- privateString itemName;
- privateSet<Category> categories =newHashSet<Category>();
配置文件:
- <classname="Category"table="Category">
- <id name="categoryId"column="CATEGORY_ID"type="integer">
- <generatorclass="native"/>
- </id>
- <property name="name"type="string"column="name"/>
- <set name="items"table="CATEGORY_ITEM"inverse="true"cascade="save-update">
- <key column="CATEGORY_ID"></key>
- <many-to-many column="ITEM_ID"class="Item"></many-to-many>
- </set>
- </class>
- <classname="Item"table="ITEM">
- <id name="itemId"column="ITEM_ID"type="integer">
- <generatorclass="native"/>
- </id>
- <property name="itemName"type="string"column="ITEM_NAME"/>
- <set name="categories"table="CATEGORY_ITEM"cascade="save-update">
- <key column="ITEM_ID"></key>
- <many-to-many column="CATEGORY_ID"class="Category"></many-to-many>
- </set>
- </class>
插入代码:
- publicclassHelloWorld {
- publicstaticvoidmain(String[] args) {
- //insert();
- //insert2();
- insert3();
- }
- privatestaticvoidinsert() {
- Configuration configuration =newConfiguration().configure();
- SessionFactory sessionFactory = configuration.buildSessionFactory();
- Session session = sessionFactory.openSession();
- Transaction tr = session.beginTransaction();
- Item item =newItem("item");
- Category category1 =newCategory("category1");
- item.getCategories().add(category1);
- Category category2 =newCategory("category2");
- item.getCategories().add(category2);
- session.save(item);
- //Hibernate: insert into ITEM (ITEM_NAME) values (?)
- //Hibernate: insert into Category (name) values (?)
- //Hibernate: insert into Category (name) values (?)
- //Hibernate: insert into CATEGORY_ITEM (ITEM_ID, CATEGORY_ID) values (?, ?)
- //Hibernate: insert into CATEGORY_ITEM (ITEM_ID, CATEGORY_ID) values (?, ?)
- tr.commit();
- session.close();
- sessionFactory.close();
- }
- privatestaticvoidinsert2() {
- Configuration configuration =newConfiguration().configure();
- SessionFactory sessionFactory = configuration.buildSessionFactory();
- Session session = sessionFactory.openSession();
- Transaction tr = session.beginTransaction();
- Category category =newCategory("category1");
- Item item1 =newItem("item1");
- category.getItems().add(item1);
- Item item2 =newItem("item1");
- category.getItems().add(item2);
- session.save(category);
- //inverse="true"的一端,忽略对中间库的影响
- //Hibernate: insert into Category (name) values (?)
- //Hibernate: insert into ITEM (ITEM_NAME) values (?)
- //Hibernate: insert into ITEM (ITEM_NAME) values (?)
- tr.commit();
- session.close();
- sessionFactory.close();
- }
- privatestaticvoidinsert3() {
- Configuration configuration =newConfiguration().configure();
- SessionFactory sessionFactory = configuration.buildSessionFactory();
- Session session = sessionFactory.openSession();
- Transaction tr = session.beginTransaction();
- Category category =newCategory("category1");
- Item item1 =newItem("item1");
- category.getItems().add(item1);
- Item item2 =newItem("item1");
- category.getItems().add(item2);
- //必须存在,才会影响中间表,因为inverse="false"的一端,才会影响中间库的变化
- item1.getCategories().add(category);
- item2.getCategories().add(category);
- session.save(category);
- //Hibernate: insert into Category (name) values (?)
- //Hibernate: insert into ITEM (ITEM_NAME) values (?)
- //Hibernate: insert into ITEM (ITEM_NAME) values (?)
- //Hibernate: insert into CATEGORY_ITEM (ITEM_ID, CATEGORY_ID) values (?, ?)
- //Hibernate: insert into CATEGORY_ITEM (ITEM_ID, CATEGORY_ID) values (?, ?)
- tr.commit();
- session.close();
- sessionFactory.close();
- }
- }