我才刚刚开始学习Spring和JPA,我碰到了一堵墙,试图找出导致我的应用尝试多次插入级联值的原因。我有以下结构:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<artifactId>core</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>core</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Person.java
@Data
@Entity
@EqualsAndHashCode(callSuper = false)
@Table(name = "persons")
public class Person extends Auditable<String> {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id", unique = true, nullable = false)
private Long id;
private String firstName;
private String lastName;
@ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE, CascadeType.PERSIST} )
@JoinColumn(name = "hometown_id")
private City hometown;
protected Person() {
}
public Person(String firstName, String lastName, City hometown) {
this.firstName = firstName;
this.lastName = lastName;
this.hometown = hometown;
}
}
City.java
@Data
@Entity
@EqualsAndHashCode(callSuper=false)
@Table(name = "cities")
public class City extends Auditable<String> {
@Id
@Size(max = 255)
@Column(name = "city_name", unique = true)
private String cityName;
@OneToMany(mappedBy = "hometown", cascade = CascadeType.ALL)
@EqualsAndHashCode.Exclude
private Set<Person> inhabitants = new HashSet<>();
protected City() {}
public City(String cityName) {
this.cityName = cityName;
}
}
我的存储库:
@RepositoryRestResource(exported = false)
public interface CityRepository extends PagingAndSortingRepository<City, Long> {}
@RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {}
所以,我第一次做以下事情:
REQUEST:
curl -X POST \
http://localhost:8080/api/persons \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-d '{
"firstName": John",
"lastName": "Doe",
"hometown": {
"cityName": "Los Angeles"
}
}'
RESPONSE:
{
"firstName": John",
"lastName": "Doe",
"qualifications": "asdasdasdasd",
"hometown": {},
"_links": {
"self": {
"href": "http://localhost:8080/api/persons/1"
},
"person": {
"href": "http://localhost:8080/api/persons/1"
}
}
}
如果从/ api / persons刷新列表,则会发现存在正确的关系。
{
"firstName": John",
"lastName": "Doe",
"qualifications": "asdasdasdasd",
"hometown": {
"_links": {
"requisitions": {
"href": "http://localhost:8080/api/requisitions/3"
}
}
},
"_links": {
"self": {
"href": "http://localhost:8080/api/persons/1"
},
"person": {
"href": "http://localhost:8080/api/persons/1"
}
}
}
但是我尝试在同一家乡坚持住的第二个人,抛出了一个错误:
REQUEST:
curl -X POST \
http://localhost:8080/api/persons \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-d '{
"firstName": Jane",
"lastName": "Doe",
"hometown": {
"cityName": "Los Angeles"
}
}'
RESPONSE:
{
"cause": {
"cause": {
"cause": null,
"message": "Duplicate entry 'Los Angeles' for key 'PRIMARY'"
},
"message": "could not execute statement"
},
"message": "could not execute statement; SQL [n/a]; constraint [PRIMARY]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement"
}
如何配置关系以仅持久保存一个新版本(如果该版本尚不存在)?我知道我可以通过首先调用多个API来查找/持久化城市来做到这一点,但是那样一来,太多的网络跃点就无法保存一小部分数据。
另一个问题,我在hometown列上将fetchType设置为EAGER,但是我仍然只得到一个链接。有没有办法让它在发送数据之前预先解析数据?
谢谢!
答案 0 :(得分:0)
这种关系对我来说似乎很奇怪,但是完全由您决定,您看到的错误原因是不言而喻的,您试图为不同用户保留同一实体,而您应该做的却是为同一个人保留许多人市。像这样
相应地更新您的请求和控制器curl -X POST \
http://localhost:8080/api/persons \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-d '{
"cityName": "Los Angeles",
"inhabitants": [
{
"firstName": John",
"lastName": "Doe",
"qualifications": "asdasdasdasd"
},
{
"firstName": Jane",
"lastName": "Doe",
}
]
}
答案 1 :(得分:0)
您可以在没有构造函数的情况下尝试吗?我使用相同类型的代码,Lombok @Data应该使这些部分无用:
301
也许这对您的项目不利,但是我认为您应该请求从名称中获取城市,然后使用城市ID(而不是城市名称)向该城市添加人,因为在您的模型中,城市名称是ID。