春季,JPA和密钥“ PRIMARY”的“休息和重复”条目

时间:2018-08-08 12:52:01

标签: java spring jpa

我才刚刚开始学习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,但是我仍然只得到一个链接。有没有办法让它在发送数据之前预先解析数据?

谢谢!

2 个答案:

答案 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。