当前位置: 首页 > 知识库问答 >
问题:

Spring Data JPA不持久化oneToMany列表

黎阳冰
2023-03-14

我正在创建一个新项目,并使用Spring Data JPA创建一些RESTendpoint。

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

只要json文件没有任何oneToMany数据,我就可以将其放到并持久化到我的主类(customer)中。然而,当张贴给客户,如果有任何数据,我会得到错误。

@Entity
@Table(name="customer")
@EntityListeners(AuditingEntityListener.class)
public class Customer extends Auditable<String> {

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private int id;
@Column(name="first_name")
private String firstName;
@Column(name="last_name")
private String lastName;

@OneToMany(fetch=FetchType.LAZY, mappedBy="customer", cascade={CascadeType.ALL})
private List<EmailAddress> emailAddresses;
.......

那些电子邮件

@Table(name="email_address")
@EntityListeners(AuditingEntityListener.class)
public class EmailAddress extends Auditable<String> {

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private int id;
@Column(name="email_type")
private byte emailType;
@Column(name="email")
private String email;

@ManyToOne(fetch=FetchType.LAZY, cascade={CascadeType.ALL})
@JoinColumn(name="customer_id")
@JsonIgnore
private Customer customer;
.....

postman json测试

{
    "id": 1,
    "firstName": "Bobby",
    "lastName": "Smith",
    "emailAddresses": [
        {
            "id": 1,
            "emailType": 1,
            "email": "bobby@bobby.com",
        },
        {
            "id": 2,
            "emailType": 1,
            "email": "bobby@gmail.com",
        }
    ]

}

顺便说一句,我已经确认在客户控制器中,电子邮件包括在客户的请求体中。

客户控制器

@PutMapping("/customers")
public Customer updateCustomer(@RequestBody Customer theCustomer) {

System.out.println("****email count "+theCustomer.getEmailAddresses().size());
for(EmailAddress index: theCustomer.getEmailAddresses()) {
System.out.println(index.toString());
}

customerService.save(theCustomer);

return theCustomer;
}
@Override
public void save(Customer theCustomer) {
//Validate the input
if(theCustomer == null) {
throw new CustomerNotFoundException("Did not find the Customer, was null...");
}
customerRepository.save(theCustomer);
}
--
-- Table structure for table `customer`
--
DROP TABLE IF EXISTS `customer`;
CREATE TABLE `customer` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(24) COLLATE utf8_bin NOT NULL,
`last_name` varchar(24) COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Primary Customer Table';

--
-- Table structure for table `email_address`
--
DROP TABLE IF EXISTS `email_address`;
CREATE TABLE `email_address` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `email_type` tinyint(4) unsigned NOT NULL COMMENT 'email type',
  `email` varchar(128) COLLATE utf8_bin NOT NULL COMMENT 'email address',
  `customer_id` int(11) NOT NULL COMMENT 'foreign key',
  INDEX par_ind (customer_id),
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`),
  KEY FK_EMAIL_CUSTOMER_idx (customer_id),
  CONSTRAINT FK_EMAIL_CUSTOMER FOREIGN KEY (customer_id) REFERENCES customer (id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='email addresses';
{
    "status": 400,
    "message": "could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement",
    "timeStamp": 1566840491483
}
****email count 2
EmailAddress [id=1, type=1, email=bobby@bobby.com]
EmailAddress [id=2, type=1, email=bobby@gmail.com]
2019-08-28 17:33:07.625  WARN 8669 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1048, SQLState: 23000
2019-08-28 17:33:07.626 ERROR 8669 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper   : Column 'customer_id' cannot be null
2019-08-28 17:33:07.629 ERROR 8669 --- [nio-8080-exec-2] o.h.i.ExceptionMapperStandardImpl        : HHH000346: Error during managed flush [org.hibernate.exception.ConstraintViolationException: could not execute statement]
2019-08-28 17:33:07.735  WARN 8669 --- [nio-8080-exec-2] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement]

因此,对于post或put,我不确定为什么Spring Data JPA save不能满足具有oneToMany关系的实体的外键约束。我猜这可能是一些缺少的注释,或者是我的sql脚本出了问题。不确定为什么更新数据没有持久化到email_address表。emailAddress实体是否需要某种类型的customer_id getter/setter?

共有1个答案

邓威
2023-03-14
public class Customer extends Auditable<String> {


    @OneToMany(fetch=FetchType.LAZY, mappedBy="customer", cascade={CascadeType.ALL})
    private List<EmailAddress> emailAddresses;

} 

public class EmailAddress extends Auditable<String> {

   @ManyToOne(fetch=FetchType.LAZY, cascade={CascadeType.ALL})
   @JoinColumn(name="customer_id")

   private Customer customer;
}

这里的mappedby意味着Customer和EmailAddress之间的关系(即Custome表中Customer_ID的值)是由emailadress#cutomer确定的,而不是由Custome#emailadress确定的。

您试图显示的内容只是customer#emailaddress的内容,Hibernate在决定为该关系更新/插入哪些DB值时会忽略这些内容。因此,必须确保emailaddress#customer设置正确。

例如,您可以使用以下方法向客户添加电子邮件地址

 public class Customer {


    @OneToMany(fetch=FetchType.LAZY, mappedBy="customer", cascade={CascadeType.ALL})
    private List<EmailAddress> emailAddresses;


    public void addEmailAddress(EmailAddress email){
          //As said Hibernate will ignore it when persist this relationship.
          //Add it mainly for the consistency of this relationship for both side in the Java instance 
         this.emailAddresses.add(email);

         email.setCustomer(this);
    }

 }

并且总是调用addeMailAddress()为客户添加电子邮件。您可以将相同的想法应用于更新客户的电子邮件地址。

 类似资料:
  • 我目前正在修补一个简单的HTTP资源。我的模型由多个“果实”的“树”组成。两者都继承自PanacheEntity。 水果 资源: 这是我通过Swagger发送的帖子请求 水果总是空的。检查postgres数据库会发现Fruit中的所有“tree_id”列都为NULL。我很确定这是一个初学者的问题,但是在检查了多个示例之后,我就是找不到我的代码有什么问题。

  • Akka持久化使有状态的actor能留存其内部状态,以便在因JVM崩溃、监管者引起,或在集群中迁移导致的actor启动、重启时恢复它。Akka持久化背后的关键概念是持久化的只是一个actor的内部状态的的变化,而不是直接持久化其当前状态 (除了可选的快照)。这些更改永远只能被附加到存储,没什么是可变的,这使得高事务处理率和高效复制成为可能。有状态actor通过重放保存的变化来恢复,从而使它们可以重

  • Serenity 2.1.5 引入保存如下信息的网格列表设置: 可见列和显示顺序 列宽 排序的列 高级过滤器(由右下角的编辑过滤器链接创建) 快速过滤器(撰写本文档时,尚未提供该功能) 包含已删除的状态切换 默认情况下,网格列表不会自动持久化任何东西。 因此,如果你隐藏某些列并离开订单页面,当你再次返回该页面时,你就会看到那些隐藏的列再次成为可见列。 你需要开启所有网格列表的持久化设置,或设置单独

  • 我正在使用网络逻辑10.3。我正在尝试配置一个持久订阅,其中包含由 jdbc 存储(在 Oracle DB 中)支持的持久消息。我有一个主题,MDB 正在作为持久订阅者侦听该主题。在场景-1下:如果我发送消息,它会命中MDB。 在场景2中:我挂起了MDB,希望发送到主题的消息只要不被MDB(它是唯一注册的持久订阅者)使用,就会一直存在。但是当我向主题发送消息时,它短暂地出现在那里,然后就消失了(我

  • Spark通过在操作中将其持久保存在内存中,提供了一种处理数据集的便捷方式。在持久化RDD的同时,每个节点都存储它在内存中计算的任何分区。也可以在该数据集的其他任务中重用它们。 我们可以使用或方法来标记要保留的RDD。Spark的缓存是容错的。在任何情况下,如果RDD的分区丢失,它将使用最初创建它的转换自动重新计算。 存在可用于存储持久RDD的不同存储级别。通过将对象(Scala,Java,Pyt

  • Redis 支持持久化,即把数据存储到硬盘中。 Redis 提供了两种持久化方式: RDB 快照(snapshot) - 将存在于某一时刻的所有数据都写入到硬盘中。 只追加文件(append-only file,AOF) - 它会在执行写命令时,将被执行的写命令复制到硬盘中。 这两种持久化方式既可以同时使用,也可以单独使用。 将内存中的数据存储到硬盘的一个主要原因是为了在之后重用数据,或者是为了防