添加Spring Data Repository会禁用功能

时间:2017-06-20 19:05:51

标签: spring spring-data spring-data-jpa

我有这两个实体。

Name    Size    Records
File1   1013    105727
File2   990     104230
File3   1520    110542
File4   2198    115369
File5   2026    113430
File6   1844    112105
File7   1216    108159
File8   1520    110408
File9   1013    105359
File10  1317    108428
File11  1925    112553
File12  1449    109456
File13  1803    111436
File14  2036    115937
File15  2043    116383
@Entity
public class Person {

@Id @GeneratedValue
private Long id;
private String name;

@ManyToOne(cascade=CascadeType.ALL)
private Location location;

public Person() {

}

我也有这个控制器。

@Entity
public class Location {

@Id @GeneratedValue
private Long id;
private String place;

@OneToMany(cascade = CascadeType.ALL, mappedBy = "location")
private Set<Person> persons;


public Location() {

}

此存储库。

@Controller
public class PersonController {

private final PersonRepository repo;

public PersonController(PersonRepository repo) {
    this.repo = repo;
}

@GetMapping("/")
public String newPerson(Person person){
    return "home";

}

@PostMapping("/")
public String newPerson(Person person, BindingResult result){   
    repo.save(person);
    return "redirect:/person";
}

我也有这种支持形式。

@Repository
public interface PersonRepository extends JpaRepository<Person, Long> {

Optional<Person> findFirstByName(String name);

}

当我提交一些数据时,一切正常。保存Person对象,Location对象也是如此。

但是当我添加

<form action="#" th:action="@{/}" th:object="${person}" method="post">
        <table>
            <tr>
                <td>Name:</td>
                <td><input type="text" th:field="*{name}" /></td>

            </tr>
            <tr>
                <td>Location:</td>
                <td><input type="text" th:field="*{location}" /></td>
            </tr>
            <tr>
                <td><button type="submit">Submit</button></td>
            </tr>
        </table>
    </form>

当我提交相同的确切表单时,Location对象不会保存到数据库。为什么只添加此存储库会导致此问题,解决方案是什么?感谢。

2 个答案:

答案 0 :(得分:1)

您可以修复表单以编写位置属性的属性:

<td><input type="text" th:field="*{location.place}" /></td>

此外,您不必在存储库中添加@Repository注释。

答案 1 :(得分:1)

详细说明为什么工作起作用:

表单绑定使用ConversionService。 Spring Data从String - >注册转换链。 id type - &gt;每个存储库托管域类的实体类型。因此,当您添加存储库时,String作为Person.location的值提交将被解释为已存在位置的标识符。它将导致使用为名为location的字段提交的值的by-id查找。

这在以下场景中很方便:假设您Location基本上是数据库中保存的实例的精选列表,例如:一系列国家。他们你不想随意创建新的,而是从整体列表中选择一个,这基本上归结为必须使用下拉框而不是文本字段。

从概念上讲,不一致的基本事物是级联(因为它们表示组合,即Location是聚合的一部分)并且LocationRepository作为存储库的存在导致托管类型隐式成为聚合根(这是基本的DDD)。

这反过来意味着您必须单独处理该实例的生命周期。一个潜在的解决方案是检查绑定到Location的{​​{1}},检查具有Person的实例是否已经存在(通过place上的查询方法),如果是,用加载的那个替换绑定的那个,或者只用原始实例调用LocationRepository来创建一个新实例。

我仍然没有完全购买原始尝试创建正确的 LocationRepository.save(…),因为您的模板Spring Framework无法猜测您提交的内容为Location实际上应该是location。所以我假设您看到一个place实例正在创建,但是完全为空且Location实际上有错误,声称它无法将BindingResult表单字段转换为{{ 1}}。