JPA 存储库:当使用 findAll() 返回实体时,与@Entity 关联的对象列表为空
JPA repository: list of objects associated to @Entity is empty when Entity is returned using findAll()
我正在为使用休眠、JPArepository 和 mysql 数据库的 Web 服务编写测试。那里一切正常,数据被预加载到数据库中并且可以正确检索(当未使用测试时),但是测试失败。
测试使用内存数据库中的H2。它妥协了:
a) 将 MyEntity 保存到数据库
正在创建 MyObjects,并将它们保存到 List
正在使用关联列表创建 MyEntity
将 MyEntity 保存到存储库
b) 检查是否可以检索 MyEntity
c) 检索时检查MyEntity的关联列表是否存在
除最后一步外,一切正常。在调试时,我发现实际上当使用 findOne() 或 findAll() 从存储库中检索时,它 returns MyEntity 和一个空的 MyObjects 列表。
我认为问题出在这些调用上,因为 save() 返回的 MyEntity 对象仍然包含 MyObjects 列表。
MyObjects 通过@ManyToOne 关系关联到 MyEntity,并且 FetchType 是 LAZY,但是我添加了一个查询以便能够获取 EAGER(如此处 ) and that doesn't work either - but just to be safe I also tried changing FetchType to EAGER and that still doesn't work. (having looked at Java JPA FetchType.EAGER does not work and Hibernate one-to-many mapping eager fetch not working and Hibernate many to one eager fetching not working)
我阅读了 findOne 和 findAll 但根据这个 - When use getOne and findOne methods Spring Data JPA - 应该没问题。
我花了很长时间寻找答案,尽管我可能遗漏了一些东西,而且我也是休眠和一般编码的新手...非常感谢教育帮助!
这是我的代码:
首先是MyObjects的实体:
package com.mypackage.representation;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.persistence.*;
import java.io.Serializable;
import java.math.BigDecimal;
@Entity
@Table(name = "myobject")
public class MyObject implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "my_entity_id")
@JsonIgnore
private MyEntity myEntityId;
private BigDecimal someValue;
protected MyObject(){
}
public MyObject (BigDecimal someValue){
this.someValue = someValue;
}
// Getters and setters omitted for brevity
}
接下来,MyEntity:
package com.mypackage.representation;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.persistence.*;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
@Entity
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class MyEntity implements Serializable {
@Id
@JsonProperty("some_id")
private int someId;
@OneToMany(mappedBy = "myEntityId", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JsonProperty("myObject")
private List<MyObject> myObjects;
protected MyEntity() {
}
public MyEntity(int someId, List<MyObject> myObjects) {
this.someId = someId;
this.myObjects = myObjects;=
}
// Getters and setters omitted for brevity
}
测试代码:
package com.mypackage;
import com.mypackage.repository.MyEntityRepository;
import com.mypackage.representation.MyObject;
import com.mypackage.representation.MyEntity;
import org.hibernate.Session;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyApplicationTests {
@Rule
public final SessionFactoryRule sf = new SessionFactoryRule();
@Autowired
MyEntityRepository myEntityRepository;
@Test
public void returnsMyEntityForSomeId() {
Session session = sf.getSession();
int someId = 0;
List<MyObject> myObjects =createMyObjects();
int numberOfMyObjectsIn = myObjects.size();
MyEntity myEntity = createMyEntity(myObjects, someId);
MyEntity saved = myEntityRepository.save(myEntity);
sf.commit();
// Test that entities are loaded into database
List<MyEntity> allMyEntities = myEntityRepository.findAll();
assertNotNull(allMyEntities);
// Test that MyEntity for given someId is returned, with myObjects as assigned -- code for fetch function is below
MyEntity thisEntity = myEntityRepository.findByIdAndFetchMyObjectsEagerly(someId);
assertNotNull(thisEntity);
int numberOfMyObjectsOut = (thisEntity.getMyObjects()).size();
assertNotNull(thisEntity.getMyObjects());
// This following test fails:
assertEquals(numberOfMyObjectsIn, numberOfMyObjectsOut);
}
private List<MyObjects> createMyObjects() {
MyObject myObjectOne = new MyObject(BigDecimal.valueOf(40));
MyObject myObjectTwo = new MyObject(BigDecimal.valueOf(50));
MyObject myObjectThree = new MyObject(BigDecimal.valueOf(60));
List<MyObjects> myObjects = new ArrayList<>();
myObjects.add(myObjectOne);
myObjects.add(myObjectTwo);
myObjects.add(myObjectThree);
return myObjects;
}
private MyEntity createMyEntity(List<MyObject> myObjects, int someId) {
return new MyEntity(someId, myObjects);
}
}
这是获取函数的代码:
package com.mypackage.repository;
import com.mypackage.representation.MyEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public interface MyEntityRepository extends JpaRepository<MyEntity, Integer> {
@Query("SELECT a FROM MyEntity a LEFT JOIN FETCH a.myObjects WHERE a.someId = (:someId)")
MyEntity findByIdAndFetchMyObjectsEagerly(@Param("someId") Integer someId);
}
最后,这是我的 SessionFactoryRule:
package com.mypackage;
import com.mypackage.representation.*;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.service.ServiceRegistry;
public class SessionFactoryRule implements MethodRule {
private SessionFactory sessionFactory;
private Transaction hibernateTransaction;
private Session session;
@Override
public Statement apply(final Statement statement, FrameworkMethod method,
Object test) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
sessionFactory = createSessionFactory();
createSession();
beginTransaction();
try {
statement.evaluate();
} finally {
shutdown();
}
}
};
}
private void shutdown() {
try {
try {
try {
hibernateTransaction.rollback();
} catch (Exception ex) {
ex.printStackTrace();
}
session.close();
} catch (Exception ex) {
ex.printStackTrace();
}
sessionFactory.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
private SessionFactory createSessionFactory() {
Configuration configuration = new Configuration();
configuration.addAnnotatedClass(MyEntity.class)
.addAnnotatedClass(MyObject.class);
configuration.setProperty("hibernate.dialect",
"org.hibernate.dialect.H2Dialect");
configuration.setProperty("hibernate.connection.driver_class",
"org.h2.Driver");
configuration.setProperty("hibernate.connection.url", "jdbc:h2:mem:./db");
configuration.setProperty("hibernate.hbm2ddl.auto", "create-update");
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
System.out.println("Hibernate serviceRegistry created");
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
public Session createSession() {
session = sessionFactory.openSession();
return session;
}
public void commit() {
hibernateTransaction.commit();
}
public void beginTransaction() {
hibernateTransaction = session.beginTransaction();
}
public Session getSession() {
return session;
}
}
MyEntity.myObjects
是关联的 inverse 端,仅将 MyObject
的实例放入列表中 not建立MyEntity
和MyObject
之间的关联。您绝对需要将 MyObject.myEntityId
设置为适当的值,因为这是关联的 owning 方。
作为旁注,如果您希望能够保存从前端收到的 MyEntity
以及稍后的子列表,请考虑使用 @JsonBackreference
而不是 @JsonIgnore
在 MyObject.myEntityId
上。这样,将不再需要手动设置 MyObject.myEntityId
。
请记住,除非您将提取类型更改为 EAGER
,否则您可能不会在调试器中看到 MyObject
的填充列表。您可能需要显式调用 MyEntity.getMyObjects()
才能实际加载列表。
我正在为使用休眠、JPArepository 和 mysql 数据库的 Web 服务编写测试。那里一切正常,数据被预加载到数据库中并且可以正确检索(当未使用测试时),但是测试失败。
测试使用内存数据库中的H2。它妥协了:
a) 将 MyEntity 保存到数据库
正在创建 MyObjects,并将它们保存到 List
正在使用关联列表创建 MyEntity
将 MyEntity 保存到存储库
b) 检查是否可以检索 MyEntity
c) 检索时检查MyEntity的关联列表是否存在
除最后一步外,一切正常。在调试时,我发现实际上当使用 findOne() 或 findAll() 从存储库中检索时,它 returns MyEntity 和一个空的 MyObjects 列表。
我认为问题出在这些调用上,因为 save() 返回的 MyEntity 对象仍然包含 MyObjects 列表。
MyObjects 通过@ManyToOne 关系关联到 MyEntity,并且 FetchType 是 LAZY,但是我添加了一个查询以便能够获取 EAGER(如此处
我花了很长时间寻找答案,尽管我可能遗漏了一些东西,而且我也是休眠和一般编码的新手...非常感谢教育帮助!
这是我的代码: 首先是MyObjects的实体:
package com.mypackage.representation;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.persistence.*;
import java.io.Serializable;
import java.math.BigDecimal;
@Entity
@Table(name = "myobject")
public class MyObject implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "my_entity_id")
@JsonIgnore
private MyEntity myEntityId;
private BigDecimal someValue;
protected MyObject(){
}
public MyObject (BigDecimal someValue){
this.someValue = someValue;
}
// Getters and setters omitted for brevity
}
接下来,MyEntity:
package com.mypackage.representation;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.persistence.*;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
@Entity
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class MyEntity implements Serializable {
@Id
@JsonProperty("some_id")
private int someId;
@OneToMany(mappedBy = "myEntityId", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JsonProperty("myObject")
private List<MyObject> myObjects;
protected MyEntity() {
}
public MyEntity(int someId, List<MyObject> myObjects) {
this.someId = someId;
this.myObjects = myObjects;=
}
// Getters and setters omitted for brevity
}
测试代码:
package com.mypackage;
import com.mypackage.repository.MyEntityRepository;
import com.mypackage.representation.MyObject;
import com.mypackage.representation.MyEntity;
import org.hibernate.Session;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyApplicationTests {
@Rule
public final SessionFactoryRule sf = new SessionFactoryRule();
@Autowired
MyEntityRepository myEntityRepository;
@Test
public void returnsMyEntityForSomeId() {
Session session = sf.getSession();
int someId = 0;
List<MyObject> myObjects =createMyObjects();
int numberOfMyObjectsIn = myObjects.size();
MyEntity myEntity = createMyEntity(myObjects, someId);
MyEntity saved = myEntityRepository.save(myEntity);
sf.commit();
// Test that entities are loaded into database
List<MyEntity> allMyEntities = myEntityRepository.findAll();
assertNotNull(allMyEntities);
// Test that MyEntity for given someId is returned, with myObjects as assigned -- code for fetch function is below
MyEntity thisEntity = myEntityRepository.findByIdAndFetchMyObjectsEagerly(someId);
assertNotNull(thisEntity);
int numberOfMyObjectsOut = (thisEntity.getMyObjects()).size();
assertNotNull(thisEntity.getMyObjects());
// This following test fails:
assertEquals(numberOfMyObjectsIn, numberOfMyObjectsOut);
}
private List<MyObjects> createMyObjects() {
MyObject myObjectOne = new MyObject(BigDecimal.valueOf(40));
MyObject myObjectTwo = new MyObject(BigDecimal.valueOf(50));
MyObject myObjectThree = new MyObject(BigDecimal.valueOf(60));
List<MyObjects> myObjects = new ArrayList<>();
myObjects.add(myObjectOne);
myObjects.add(myObjectTwo);
myObjects.add(myObjectThree);
return myObjects;
}
private MyEntity createMyEntity(List<MyObject> myObjects, int someId) {
return new MyEntity(someId, myObjects);
}
}
这是获取函数的代码:
package com.mypackage.repository;
import com.mypackage.representation.MyEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public interface MyEntityRepository extends JpaRepository<MyEntity, Integer> {
@Query("SELECT a FROM MyEntity a LEFT JOIN FETCH a.myObjects WHERE a.someId = (:someId)")
MyEntity findByIdAndFetchMyObjectsEagerly(@Param("someId") Integer someId);
}
最后,这是我的 SessionFactoryRule:
package com.mypackage;
import com.mypackage.representation.*;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.service.ServiceRegistry;
public class SessionFactoryRule implements MethodRule {
private SessionFactory sessionFactory;
private Transaction hibernateTransaction;
private Session session;
@Override
public Statement apply(final Statement statement, FrameworkMethod method,
Object test) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
sessionFactory = createSessionFactory();
createSession();
beginTransaction();
try {
statement.evaluate();
} finally {
shutdown();
}
}
};
}
private void shutdown() {
try {
try {
try {
hibernateTransaction.rollback();
} catch (Exception ex) {
ex.printStackTrace();
}
session.close();
} catch (Exception ex) {
ex.printStackTrace();
}
sessionFactory.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
private SessionFactory createSessionFactory() {
Configuration configuration = new Configuration();
configuration.addAnnotatedClass(MyEntity.class)
.addAnnotatedClass(MyObject.class);
configuration.setProperty("hibernate.dialect",
"org.hibernate.dialect.H2Dialect");
configuration.setProperty("hibernate.connection.driver_class",
"org.h2.Driver");
configuration.setProperty("hibernate.connection.url", "jdbc:h2:mem:./db");
configuration.setProperty("hibernate.hbm2ddl.auto", "create-update");
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
System.out.println("Hibernate serviceRegistry created");
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
public Session createSession() {
session = sessionFactory.openSession();
return session;
}
public void commit() {
hibernateTransaction.commit();
}
public void beginTransaction() {
hibernateTransaction = session.beginTransaction();
}
public Session getSession() {
return session;
}
}
MyEntity.myObjects
是关联的 inverse 端,仅将 MyObject
的实例放入列表中 not建立MyEntity
和MyObject
之间的关联。您绝对需要将 MyObject.myEntityId
设置为适当的值,因为这是关联的 owning 方。
作为旁注,如果您希望能够保存从前端收到的 MyEntity
以及稍后的子列表,请考虑使用 @JsonBackreference
而不是 @JsonIgnore
在 MyObject.myEntityId
上。这样,将不再需要手动设置 MyObject.myEntityId
。
请记住,除非您将提取类型更改为 EAGER
,否则您可能不会在调试器中看到 MyObject
的填充列表。您可能需要显式调用 MyEntity.getMyObjects()
才能实际加载列表。