Reprinted from: https://blog.csdn.net/blueheart20/article/details/52912023
Introduction: JPA is a very popular and commonly used persistence framework standard, under which several different implementations can be connected. In different parent-child table management, we often encounter the problem of no session. How to solve it
The introduction of the problem
In the JPA based unit test, we use JUnit to read the association table information of the test database, and the result is as follows:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.rain.wx.meal.model.DishCategory.dishes, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:587)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:204)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:566)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:135)
at org.hibernate.collection.internal.PersistentBag.get(PersistentBag.java:449)
at com.rain.wx.meal.service.DishServiceTest.testDishes(DishServiceTest.java:86)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
After analysis, the key words are: could not initialize proxy – no session; Based on the implementation of JPA, when accessing the database, the current access and operation session for the database has been closed and released, so it prompts that no session is available
code implementation analysis
let’s take a look at the specific code
@Entity
@Table(name="dish_category")
@Data
@EqualsAndHashCode(callSuper=false)
@JsonRootName(value="category")
public class DishCategory extends BaseEntity {
private static final long serialVersionUID = -7189824224534351030L;
@Column
private String name;
@Column
private String description;
@OneToMany
@JoinColumn(name="category_id",referencedColumnName="id")
private List<MealDish> dishes;
}
The other entity bean is mealdish, and its code is:
@Entity
@Table(name = "dish")
@JsonRootName(value="dish")
//@Lazy(value=false)
public class MealDish extends BaseEntity {
private static final long serialVersionUID = -3982356728880195795L;
@Column
private String name;
@Column
private float price;
@Column(name = "img_url")
private String imgUrl;
@Column(name="category_id")
private long categoryId;
@Column
private boolean active;
@Column
private int soldCount;
.......
}
The codes of repository/service are as follows:
@Service
public class DishServiceImpl implements DishService {
@Transactional
@Override
public List<DishCategory> getDishCategory() {
return this.dishCategoryRepo.findAll();
}
........
}
Repository related code is empty code, no actual implementation, again ignored here
Unit test code content:
@Test
public void testDishes() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
List<DishCategory> categories = this.dishService.getDishCategory();
for (DishCategory category : categories) {
// log.debug(String.valueOf(category.getDishes().get(0)));
}
String jsonStr = mapper.writeValueAsString(categories);
log.info(jsonStr);
}
Problem analysis
Based on the understanding of hibernate and JPA, in ORM, lazy loading is used in order to improve the performance, that is, additional data will be loaded when it is used. As a result, the session fails when the data is loaded again when it is used. So the goal of the problem is to load the data ahead of time
solution to the problem
try 1: add @ transactional to the service method to add transactions
result 1: invalid
try 2: on the @ onetomany method, Use @ lazy (false)
result 2: invalid
try 3: use fetch = fetchtype = Eagle
in the parameter of @ onetomony result 3: problem solving
try 4: add spring.jpa.open-in-view = true
in the configuration file of application.properties result 4: correct code content of problem solving
method 3:
@Entity
@Table(name="dish_category")
@Data
@EqualsAndHashCode(callSuper=false)
@JsonRootName(value="category")
public class DishCategory extends BaseEntity {
private static final long serialVersionUID = -7189824224534351030L;
@Column
private String name;
@Column
private String description;
@OneToMany(fetch=FetchType.EAGER)
@JoinColumn(name="category_id",referencedColumnName="id")
private List<MealDish> dishes;
}
Explanation and explanation of method 4:
This setting option:
spring.jpa.open-in-view=true
In fact, the previous openentitymanagerinview interceptor solved the problem of declaration cycle of session between spring MVC and JPA
summary
the core problem is to solve the problem of delayed loading. In order to load in time, loading in time will consume a certain amount of resources. Please pay attention to the problem of reducing the performance of the program