【问题标题】:JpaRepository.findOne()'s influence on JpaRepository.getOne()JpaRepository.findOne() 对 JpaRepository.getOne() 的影响
【发布时间】:2017-07-11 10:14:36
【问题描述】:

我在下面的例子中使用了 spring boot、spring web 和 spring data。

我有一个名为 Person 的实体,并且我已经在数据库中填充了两个 Persons:

个人实体

@Entity
public class Person {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
private long id;
private String name;

public long getId() {
    return id;
}
public void setId(long id) {
    this.id = id;
}
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}
public Personne() {
}
public Personne(long id, String name) {
    this.id = id;
    this.name = name;
}}

PersonRepository

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

PersonController

@RestController
public class PersonController {

@Autowired
private PersonRepository personRepo;

@RequestMapping(value = "/perss/{id}")
public Person getById(@PathVariable("id") long id) {
    return personRepo.xxxx(id);
}}

用例 1

当我用 personRepo.getOne(id) 替换 personRepo.xxxx(id) 并点击 localhost:8080/perss/1 时,由于 getOne() 方法,我在浏览器中收到 Could not write JSON: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) 错误将代理返回到杰克逊以某种方式无法转换的 Person 。

用例 2

当我将 personRepo.xxxx(id) 替换为 personRepo.findOne(id) 并点击 localhost:8080/perss/1 时,我会以正确的 JSON 格式获得所需的 Person 对象(这个可以正常工作)。

用例 3

当我将PersonController getById() 方法的代码替换为以下代码时:

@RequestMapping(value = "/perss/{id}")
public Person getById(@PathVariable("id") long id) {
    Person p1 = personRepo.findOne(id);
    Person p2 = personRepo.getOne(id);
    return p2;
}

然后点击localhost:8080/perss/1,我会得到正确 JSON 格式的所需 Person 对象。

问题:

使用getOne() 会出错,但同时使用findOne()getOne() 会得到很好的结果。

findOne() 如何影响getOne() 的行为。

编辑

用例 4

当我颠倒 p1 和 p2 的顺序时,我得到一个错误。

@RequestMapping(value = "/perss/{id}")
public Person getById(@PathVariable("id") long id) {
    Person p2 = personRepo.getOne(id);
    Person p1 = personRepo.findOne(id);
    return p2;
}

【问题讨论】:

标签: java hibernate jackson spring-data


【解决方案1】:

尝试返回p1,你可能会得到同样的错误。

@RequestMapping(value = "/perss/{id}")
public Person getById(@PathVariable("id") long id) {
    Person p1 = personRepo.findOne(id);
    Person p2 = personRepo.getOne(id);
    return p1;
}

你没有得到任何东西,因为你没有序列化 p1JavassistLazyInitializer 代理。你序列化了p2,这已经很好了。

这个也可以:

@RequestMapping(value = "/check/{id}")
public void getById(@PathVariable("id") long id) {
    personRepo.getOne(id);
}

当对象从 POJO 转换为 JSON 时发生 JSON 序列化。

具有延迟初始化属性的 bean 的序列化错误是因为序列化发生在它们完全加载之前。

您可以尝试使用findOne() 执行以下选项来修复错误:

  1. 将下面的属性设置为您的application.properties 文件(如异常消息所示):

    spring.jackson.serialization.fail-on-empty-beans=false

  2. 使用惰性初始化属性注释实体,例如:

    @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})

所以,回答这个问题:

findOne() 如何影响 getOne() 的行为。

它没有。而且对存储库的调用也不会调用 JSON 序列化过程。

【讨论】:

    【解决方案2】:

    您是正确的,调用顺序确实在同时使用findOne()getOne() 时会影响结果。

    简短回答:这两种方法都会首先在持久化上下文中查找 ID,如果存在则返回缓存值。如果在持久性上下文中没有找到任何东西,他们将继续加载他们喜欢的结果并缓存它。缓存的值会在下次运行时被其他方法找到。

    • 如果id 不在持久化上下文中,getOne(id) 将加载(并缓存)代理。
    • 如果id 不在持久化上下文中,findOne(id) 将加载(并缓存)裸实体。

    长答案:我遇到了同样的问题,我的项目使用 Hibernate 5.2.4.Final。正在发生的事情的细节涉及一些 Hibernate 代码。经过一段时间的调试,我发现findOne()getOne()最终都调用了Hibernate的DefaultLoadEventListener.onLoad()方法,但是他们用不同的loadType参数调用它:

    • getOne() 最终委托给SessionImpl.IdentifierLoadAccessImpl&lt;T&gt;.doGetReference(),它指定了LoadEventListener.LOAD 的loadType,最终传递给DefaultLoadEventListener.proxyOrLoad()LoadEventListener.LOAD确实允许创建代理。
    • findOne() 最终委托给 SessionImpl.IdentifierLoadAccessImpl&lt;T&gt;.doLoad(),它指定了 LoadEventListener.GET 的 loadType 值,最终传递给 DefaultLoadEventListener.proxyOrLoad()LoadEventListener.GET不允许允许创建代理。

    DefaultLoadEventListener.proxyOrLoad() 中设置断点,以验证传入的 LoadType options 参数是否具有不同的 allowProxyCreation 字段值,具体取决于调用的是 findOne() 还是 getOne()它。

    可以看到如果allowProxyCreation为真且没有代理,那么proxyOrLoad()会返回createProxyIfNecessary()的结果。在使用getOne()的情况下,这将导致返回一个代理。

    如果碰巧在getOne() 之前针对相同的实体类型和ID 调用了findOne(),那么当getOne() 调用进入createProxyIfNecessary() 时,它将提前返回,因为该实体已经在持久性上下文。在这种情况下,调用 getOne()不会导致创建代理。

    如果您在findOne() 之前调用getOne(),则将创建代理并将其存储在持久性上下文中,并且findOne() 还将返回代理,因为它只会从持久性上下文中检索缓存的代理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-18
      • 2016-11-06
      • 2012-06-12
      • 2020-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-05
      相关资源
      最近更新 更多