我在网上查了很多帖子,但都没有解决我的问题。如果有人能提供帮助,真的很感激! 我有OneToMany父子关系。当其中一个子组合键是父组件(Level1)的外键时,Hibernate不会级联插入子级(Level2)。我指定了cascade =“all”,无论逆是true还是false,结果都是一样的。没有例外,它只是不插入。下面的打印显示Level1已成功插入,但Level2仅被选中,没有插入。
Hibernate:插入sst.level_1(STATUS,NAME)值(?,?) Hibernate:从sst.level_2 level2x_中选择level2x_.LEVEL_1_ID,level2x_.TIMESEGMENT,level2x_.CATEGORY为CATEGORY4_,其中level2x_.LEVEL_1_ID =?和level2x_.TIMESEGMENT =?
API是Hibernate 4.1.6 / Spring 3.1.2。
这是mySQL的表定义:
CREATE TABLE level_1
(
ID int NOT NULL PRIMARY KEY AUTO_INCREMENT,
STATUS int,
NAME varchar(255)
);
CREATE TABLE level_2
(
LEVEL_1_ID int,
TIMESEGMENT int,
CATEGORY varchar(2),
PRIMARY KEY (LEVEL_1_ID, TIMESEGMENT),
FOREIGN KEY (LEVEL_1_ID) REFERENCES level_1(ID) ON DELETE CASCADE
);
这是测试代码。
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
doInsert(context);
}
private static void doInsert(ApplicationContext context) {
Level1 level1 = new Level1();
Level2 level2 = new Level2();
level1.setName("LEVEL 1 NAME");
level1.setStatus(1);
level1.getLevel2s().add(level2);
level2.setLevel1(level1);
level2.setCategory("CA");
level2.setId(new Level2Id(level1.getId(), 10));
Level1DAO level1DAO = (Level1DAO) context.getBean("Level1DAO");
level1DAO.save(level1);
}
}
Level1的映射:
<hibernate-mapping>
<class name="com.jc.hibernate.Level1" table="level_1" catalog="sst">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="identity" />
</id>
<property name="status" type="java.lang.Integer">
<column name="STATUS" />
</property>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<set name="level2s" inverse="true" cascade="all">
<key>
<column name="LEVEL_1_ID" not-null="true" />
</key>
<one-to-many class="com.jc.hibernate.Level2" />
</set>
</class>
</hibernate-mapping>
Level2的映射:
<hibernate-mapping>
<class name="com.jc.hibernate.Level2" table="level_2" catalog="sst">
<composite-id name="id" class="com.jc.hibernate.Level2Id">
<key-property name="level1Id" type="java.lang.Integer">
<column name="LEVEL_1_ID" />
</key-property>
<key-property name="timesegment" type="java.lang.Integer">
<column name="TIMESEGMENT" />
</key-property>
</composite-id>
<many-to-one name="level1" class="com.jc.hibernate.Level1" update="false" insert="false" fetch="select">
<column name="LEVEL_1_ID" not-null="true" />
</many-to-one>
<property name="category" type="java.lang.String">
<column name="CATEGORY" length="2" />
</property>
</class>
</hibernate-mapping>
答案 0 :(得分:1)
您的映射似乎是正确的。
你的问题看起来像是行
level2.setId(new Level2Id(level1.getId(), 10));
执行此行时,level1仍然没有为其分配id,因此它将为null。
由于level_1_id
和段是复合主键,因此它们都不能为空。
所以你需要先做
Integer generatedId = (Integer) Session.save(level1)
这会在数据库中立即插入并返回标识符。
然后,您可以使用标识符来创建Lever2Id
level2.setId(new Level2Id(generatedId , 10));
我在MySql上尝试了这个,你提供的映射和工作正常。
如果我先没先保存level1
,则会给我错误
Column 'LEVEL_1_ID' cannot be null
<强>更新强>
不幸的是,当子级中存在复合键时,级联不会自动,而复合键中其中一列是指父级的主键。所以你必须手动设置id和关系,如上所示。
请注意,如果Child具有单个列ID并且您的关系是一对一的话,那么Hibernate可以确定子主列来自Parent并且可以自动设置。请参阅here和here
文档也建议here
“您无法使用IdentifierGenerator生成复合键。 相反,应用程序必须分配自己的标识符。“
表示复合id映射的hibernate类是org.hibernate.mapping.Component(用于组件,复合元素,复合标识符等),默认id生成策略是“已分配”,除非您提供了自定义ID生成器通过创建您自己的CompositeUserType(对于复合ID)以及使用自定义标识符生成器({3}}将您带入hackish方式。但这看起来很丑陋而且有点矫枉过正。
关于保存父级和子级以及在事务中手动设置关系首先删除来自Parent set元素的cascade all选项或将其设置为“none”,以便不存在传递持久性休眠。保存父母只会保存父母而非子女。
然后使用您可用的所有属性准备父级和子级,并像这样设置两侧的对象关系(以确保模型一致性)。
Parent parent = new Parent();
parent.setStatus( 1 );
parent.setName( "Parent" );
ChildId childId = new ChildId();
Child child = new Child(childId);
child.setCategory( "TH" );
parent.addChild( child );
child.getId().setTimesegment( 10 );
Parent的addChild方法将child添加到集合中,并在一个方便的方法中设置子父级
public void addChild(Child child){
if (child !=null){
getChildrens().add( child );
child.setParent( this );
}
}
然后在DAO中有一个方法,它将Child作为参数,也是事务性的,就像这样(尽管更好的方法是使服务层事务而不是DAO层)
public void save(Child child){
//get Session
Session session =.......
Transaction tx = session.beginTransaction();
Integer generatedId = (Integer)session.save( child.getParent() );
ChildId childId = child.getId();
childId.setChildId( generatedId );
session1.save( child );
tx1.commit();
HibernateUtil.closeSession();
}
截至目前,这是我的建议。
答案 1 :(得分:0)
只是为了更新。我删除了复合ID。而是使用子表上的数据库生成的代理键和父ID的外键。这适用于与cascade =“all”的一对多关系。保存父级可以一步保存子级。这并不能解决原始问题,而是另一种选择。
CREATE TABLE level_11 (
ID int NOT NULL PRIMARY KEY AUTO_INCREMENT,
STATUS int, NAME varchar(255) );
CREATE TABLE level_22 ( ID int NOT NULL PRIMARY KEY AUTO_INCREMENT,
LEVEL_11_ID int NOT NULL,
TIMESEGMENT int NOT NULL,
FOREIGN KEY (LEVEL_11_ID) REFERENCES level_11(ID) ON DELETE CASCADE,
CONSTRAINT UQ_LEVEL2 UNIQUE (LEVEL_11_ID, TIMESEGMENT) );