![](/img/trans.png)
[英]How to save child object automatically using a parent object using Hibernate and JPA?
[英]Save child objects automatically using JPA Hibernate
我在 Parent 和 Child 表之间有一个一对多的关系。 在父对象中我有一个
List<Child> setChildren(List<Child> childs)
我在 Child 表中也有一个外键。 此外键是引用数据库中父行的 ID。 所以在我的数据库配置中这个外键不能为NULL。 此外键也是父表中的主键。
所以我的问题是如何通过执行以下操作自动保存子对象:
session.save(parent);
我尝试了上面的方法,但我收到一个数据库错误,抱怨 Child 表中的外键字段不能为 NULL。 有没有办法告诉 JPA 自动将这个外键设置到 Child 对象中,以便它可以自动保存子对象?
我尝试了上述操作,但出现数据库错误,抱怨 Child 表中的外键字段不能为 NULL。 有没有办法告诉 JPA 自动将此外键设置到 Child 对象中,以便它可以自动保存子对象?
嗯,这里有两件事。
首先,您需要级联保存操作(但我的理解是您正在执行此操作,否则在“子”表中插入期间不会出现 FK 约束冲突)
其次,您可能有双向关联,我认为您没有正确设置“链接的两侧”。 你应该做这样的事情:
Parent parent = new Parent();
...
Child c1 = new Child();
...
c1.setParent(parent);
List<Child> children = new ArrayList<Child>();
children.add(c1);
parent.setChildren(children);
session.save(parent);
一种常见的模式是使用链接管理方法:
@Entity
public class Parent {
@Id private Long id;
@OneToMany(mappedBy="parent")
private List<Child> children = new ArrayList<Child>();
...
protected void setChildren(List<Child> children) {
this.children = children;
}
public void addToChildren(Child child) {
child.setParent(this);
this.children.add(child);
}
}
代码变为:
Parent parent = new Parent();
...
Child c1 = new Child();
...
parent.addToChildren(c1);
session.save(parent);
参考
我相信您需要通过 xml/annotation 在映射中设置级联选项。 请参阅此处的 Hibernate 参考示例。
如果您使用注释,则需要执行以下操作,
@OneToMany(cascade = CascadeType.PERSIST) // Other options are CascadeType.ALL, CascadeType.UPDATE etc..
以下程序描述了双向关系如何在休眠中工作。
当父对象保存其子对象列表时,将自动保存。
在父母方面:
@Entity
@Table(name="clients")
public class Clients implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@OneToMany(mappedBy="clients", cascade=CascadeType.ALL)
List<SmsNumbers> smsNumbers;
}
并在子端添加以下注释:
@Entity
@Table(name="smsnumbers")
public class SmsNumbers implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
int id;
String number;
String status;
Date reg_date;
@ManyToOne
@JoinColumn(name = "client_id")
private Clients clients;
// and getter setter.
}
主要类:
public static void main(String arr[])
{
Session session = HibernateUtil.openSession();
//getting transaction object from session object
session.beginTransaction();
Clients cl=new Clients("Murali", "1010101010");
SmsNumbers sms1=new SmsNumbers("99999", "Active", cl);
SmsNumbers sms2=new SmsNumbers("88888", "InActive", cl);
SmsNumbers sms3=new SmsNumbers("77777", "Active", cl);
List<SmsNumbers> lstSmsNumbers=new ArrayList<SmsNumbers>();
lstSmsNumbers.add(sms1);
lstSmsNumbers.add(sms2);
lstSmsNumbers.add(sms3);
cl.setSmsNumbers(lstSmsNumbers);
session.saveOrUpdate(cl);
session.getTransaction().commit();
session.close();
}
在您的 setChilds 中,您可能想尝试遍历列表并执行类似的操作
child.parent = this;
您还应该将父级上的级联设置为适当的值。
以下是在双向关系的子对象中分配父对象的方法?
假设您有一个关系说一对多,那么对于每个父对象,存在一组子对象。 在双向关系中,每个子对象都将引用其父对象。
eg : Each Department will have list of Employees and each Employee is part of some department.This is called Bi directional relations.
为此,一种方法是在持久化父对象的同时在子对象中分配父对象
Parent parent = new Parent();
...
Child c1 = new Child();
...
c1.setParent(parent);
List<Child> children = new ArrayList<Child>();
children.add(c1);
parent.setChilds(children);
session.save(parent);
另一种方法是,您可以使用休眠拦截器,这种方式可以帮助您不必为所有模型编写上述代码。
在执行任何数据库操作之前,Hibernate 拦截器提供 apis 来完成您自己的工作。同样,对象的保存,我们可以使用反射在子对象中分配父对象。
public class CustomEntityInterceptor extends EmptyInterceptor {
@Override
public boolean onSave(
final Object entity, final Serializable id, final Object[] state, final String[] propertyNames,
final Type[] types) {
if (types != null) {
for (int i = 0; i < types.length; i++) {
if (types[i].isCollectionType()) {
String propertyName = propertyNames[i];
propertyName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
try {
Method method = entity.getClass().getMethod("get" + propertyName);
List<Object> objectList = (List<Object>) method.invoke(entity);
if (objectList != null) {
for (Object object : objectList) {
String entityName = entity.getClass().getSimpleName();
Method eachMethod = object.getClass().getMethod("set" + entityName, entity.getClass());
eachMethod.invoke(object, entity);
}
}
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
return true;
}
}
您可以将拦截器注册为配置
new Configuration().setInterceptor( new CustomEntityInterceptor() );
在 JPA @*To*
关系中,父实体和子实体必须在(父)保存之前交叉分配。
使用org.hibernate.annotations
进行Cascade
,如果hibernate
和JPA
一起使用,它会以某种方式抱怨保存子对象。
总之将级联类型设置为 all ,就可以了; 以您的模型为例。 像这样添加代码。 @OneToMany(mappedBy = "receipt", cascade=CascadeType.ALL) 私有列表 saleSet;
如果您没有双向关系并且只想保存/更新子表中的单列,那么您可以使用子实体创建 JPA 存储库并调用 save/saveAll 或 update 方法。
注意:如果您遇到 FK 违规,则意味着您的邮递员请求具有主键和外键 ID 与子表中生成的 ID 不匹配,请检查您要更新的请求和子表中的 ID(它们应该匹配/如果它们并不意味着您遇到 FK 违规)在事务之前保存父级和子级时生成的任何 id,当您尝试更新子表中的单个列时,这些 id 应该在您的第二次调用中匹配。
家长:
@Entity
@Table(name="Customer")
public class Customer implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private UUID customerId ;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name ="child_columnName", referencedColumnName=
"parent_columnName")
List<Accounts> accountList;
}
孩子 :
@Entity
@Table(name="Account")
public class Account implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private UUID accountid;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.