测试期间出现 ManyToMany nullpointerexception - MergedContextConfiguration ... SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]
ManyToMany nullpointerexception during test - MergedContextConfiguration ... SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]
我确信我犯了一些愚蠢的错误,但我想不通。
我想我的单元测试的这一部分不知何故搞砸了:
...
User u1 = new User();
u1.setEmail("email1");
u1.setFirstName("firstName1");
u1.setLastName("lastName1");
u1.setMobile("mobile1");
Role r1 = new Role();
r1.setDescription("description 1");
r1.setName("name1");
User userSaved = userRepository.save(u1);
r1.getUsers().add(userSaved);
我的意思是,我一定是做错了什么,首先保存实体而不填充 ManyToMany 字段,然后尝试添加它。但是我不知道我在这里做错了什么。
如果我 post 来自 PostMan,该项目将完美运行,因此超出测试目标,它已正确建模,我相信它。但是我在测试期间遇到了这个问题
2020-07-30 17:23:13.610 INFO 15464 --- [ main] com.notyfyd.JPAUnitTest : Starting JPAUnitTest on SPANOT149 with PID 15464 (started by Cast in C:\_d\Testes\Samples\jpa-demo-many-to-many-bidirectional-getters)
2020-07-30 17:23:13.613 INFO 15464 --- [ main] com.notyfyd.JPAUnitTest : No active profile set, falling back to default profiles: default
2020-07-30 17:23:13.988 INFO 15464 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in LAZY mode.
2020-07-30 17:23:14.053 INFO 15464 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 58ms. Found 2 JPA repository interfaces.
2020-07-30 17:23:14.588 INFO 15464 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2020-07-30 17:23:14.648 INFO 15464 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.4.18.Final
2020-07-30 17:23:14.793 INFO 15464 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2020-07-30 17:23:15.848 INFO 15464 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-07-30 17:23:15.937 INFO 15464 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2020-07-30 17:23:15.968 INFO 15464 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.PostgreSQL10Dialect
Hibernate: alter table if exists t_user_roles drop constraint if exists FKj47yp3hhtsoajht9793tbdrp4
Hibernate: alter table if exists t_user_roles drop constraint if exists FKp67oqi40xgs7j7ad5xqaxx857
Hibernate: drop table if exists t_role cascade
Hibernate: drop table if exists t_user cascade
Hibernate: drop table if exists t_user_roles cascade
Hibernate: create table t_role (id int8 generated by default as identity, description varchar(255), name varchar(255), primary key (id))
Hibernate: create table t_user (id int8 generated by default as identity, email varchar(255), first_name varchar(255), last_name varchar(255), mobile varchar(255), primary key (id))
Hibernate: create table t_user_roles (users_id int8 not null, roles_id int8 not null)
Hibernate: alter table if exists t_user add constraint UK_i6qjjoe560mee5ajdg7v1o6mi unique (email)
Hibernate: alter table if exists t_user_roles add constraint FKj47yp3hhtsoajht9793tbdrp4 foreign key (roles_id) references t_role
Hibernate: alter table if exists t_user_roles add constraint FKp67oqi40xgs7j7ad5xqaxx857 foreign key (users_id) references t_user
2020-07-30 17:23:16.668 INFO 15464 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-07-30 17:23:16.675 INFO 15464 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-07-30 17:23:16.813 INFO 15464 --- [ main] com.notyfyd.JPAUnitTest : Started JPAUnitTest in 3.597 seconds (JVM running for 4.482)
2020-07-30 17:23:16.927 INFO 15464 --- [ main] o.s.t.c.transaction.TransactionContext : Began transaction (1) for test context [DefaultTestContext@19e4fcac testClass = JPAUnitTest, testInstance = com.notyfyd.JPAUnitTest@68b32e3e, testMethod = should_save_an_user@JPAUnitTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@52c3cb31 testClass = JPAUnitTest, locations = '{}', classes = '{class com.notyfyd.JpaDemoApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@4b79ac84 key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@4681c175, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@6fa51cd4, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@6be968ce, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@b6cf2445, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@3de8f619, org.springframework.boot.test.context.SpringBootTestArgs@1], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@11cadb32]; rollback [true]
Hibernate: insert into t_user (email, first_name, last_name, mobile) values (?, ?, ?, ?)
2020-07-30 17:23:17.345 INFO 15464 --- [ main] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test: [DefaultTestContext@19e4fcac testClass = JPAUnitTest, testInstance = com.notyfyd.JPAUnitTest@68b32e3e, testMethod = should_save_an_user@JPAUnitTest, testException = java.lang.NullPointerException, mergedContextConfiguration = [MergedContextConfiguration@52c3cb31 testClass = JPAUnitTest, locations = '{}', classes = '{class com.notyfyd.JpaDemoApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@4b79ac84 key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@4681c175, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@6fa51cd4, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@6be968ce, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@b6cf2445, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@3de8f619, org.springframework.boot.test.context.SpringBootTestArgs@1], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]
2020-07-30 17:23:17.355 INFO 15464 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2020-07-30 17:23:17.358 INFO 15464 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2020-07-30 17:23:17.373 INFO 15464 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
JunitTest
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit4.SpringRunner;
import com.notyfyd.repository.*;
import com.notyfyd.model.*;
import com.notyfyd.entity.*;
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class JPAUnitTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
RoleRepository roleRepository;
@Autowired
UserRepository userRepository;
@Test
public void should_save_an_user() {
User u1 = new User();
u1.setEmail("email1");
u1.setFirstName("firstName1");
u1.setLastName("lastName1");
u1.setMobile("mobile1");
Role r1 = new Role();
r1.setDescription("description 1");
r1.setName("name1");
User userSaved = userRepository.save(u1);
r1.getUsers().add(userSaved); //here raises the exception
Role roleSaved = roleRepository.save(r1);
u1.getRoles().add(roleSaved);
userSaved = userRepository.save(u1);
assertNotNull(userSaved);
}
}
实体:
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "t_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
@ManyToMany(targetEntity = User.class, mappedBy = "roles", cascade = {CascadeType.PERSIST, CascadeType.DETACH,CascadeType.MERGE,CascadeType.REFRESH})
private List<User> users;
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
和
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private String mobile;
@Column(unique = true)
private String email;
@ManyToMany(targetEntity = Role.class, cascade = {CascadeType.PERSIST, CascadeType.DETACH,CascadeType.MERGE,CascadeType.REFRESH} )
private List<Role> roles;
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Pom
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<!-- version>2.2.5.RELEASE</version-->
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.notyfyd</groupId>
<artifactId>jpa-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>jpa-demo</name>
<description>Demo project for JPA</description>
<properties>
<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-web</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<!-- exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions-->
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
型号:
import java.util.List;
public class RoleModel {
private String name;
private String description;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
和
import com.notyfyd.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
public class UserModel {
private String firstName;
private String lastName;
private String mobile;
private String email;
private List<RoleModel> roles;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public List<RoleModel> getRoles() {
return roles;
}
public void setRoles(List<RoleModel> roles) {
this.roles = roles;
}
}
- 您的
role
实体 r1
未通过休眠获取,并且还不是 managed
实体,因此 r1.getUsers()
是 null
。当您调用 r1.getUsers()
时,Hibernate 对 r1
一无所知。因此,为了防止这些问题,请像下面这样修改 Role
实体并使用 new ArrayList<>();
对其进行初始化
@Entity
@Table(name = "t_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
@ManyToMany(...
private List<User> users = new ArrayList<>();
...
}
我确信我犯了一些愚蠢的错误,但我想不通。
我想我的单元测试的这一部分不知何故搞砸了:
...
User u1 = new User();
u1.setEmail("email1");
u1.setFirstName("firstName1");
u1.setLastName("lastName1");
u1.setMobile("mobile1");
Role r1 = new Role();
r1.setDescription("description 1");
r1.setName("name1");
User userSaved = userRepository.save(u1);
r1.getUsers().add(userSaved);
我的意思是,我一定是做错了什么,首先保存实体而不填充 ManyToMany 字段,然后尝试添加它。但是我不知道我在这里做错了什么。
如果我 post 来自 PostMan,该项目将完美运行,因此超出测试目标,它已正确建模,我相信它。但是我在测试期间遇到了这个问题
2020-07-30 17:23:13.610 INFO 15464 --- [ main] com.notyfyd.JPAUnitTest : Starting JPAUnitTest on SPANOT149 with PID 15464 (started by Cast in C:\_d\Testes\Samples\jpa-demo-many-to-many-bidirectional-getters)
2020-07-30 17:23:13.613 INFO 15464 --- [ main] com.notyfyd.JPAUnitTest : No active profile set, falling back to default profiles: default
2020-07-30 17:23:13.988 INFO 15464 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in LAZY mode.
2020-07-30 17:23:14.053 INFO 15464 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 58ms. Found 2 JPA repository interfaces.
2020-07-30 17:23:14.588 INFO 15464 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2020-07-30 17:23:14.648 INFO 15464 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.4.18.Final
2020-07-30 17:23:14.793 INFO 15464 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2020-07-30 17:23:15.848 INFO 15464 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-07-30 17:23:15.937 INFO 15464 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2020-07-30 17:23:15.968 INFO 15464 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.PostgreSQL10Dialect
Hibernate: alter table if exists t_user_roles drop constraint if exists FKj47yp3hhtsoajht9793tbdrp4
Hibernate: alter table if exists t_user_roles drop constraint if exists FKp67oqi40xgs7j7ad5xqaxx857
Hibernate: drop table if exists t_role cascade
Hibernate: drop table if exists t_user cascade
Hibernate: drop table if exists t_user_roles cascade
Hibernate: create table t_role (id int8 generated by default as identity, description varchar(255), name varchar(255), primary key (id))
Hibernate: create table t_user (id int8 generated by default as identity, email varchar(255), first_name varchar(255), last_name varchar(255), mobile varchar(255), primary key (id))
Hibernate: create table t_user_roles (users_id int8 not null, roles_id int8 not null)
Hibernate: alter table if exists t_user add constraint UK_i6qjjoe560mee5ajdg7v1o6mi unique (email)
Hibernate: alter table if exists t_user_roles add constraint FKj47yp3hhtsoajht9793tbdrp4 foreign key (roles_id) references t_role
Hibernate: alter table if exists t_user_roles add constraint FKp67oqi40xgs7j7ad5xqaxx857 foreign key (users_id) references t_user
2020-07-30 17:23:16.668 INFO 15464 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-07-30 17:23:16.675 INFO 15464 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-07-30 17:23:16.813 INFO 15464 --- [ main] com.notyfyd.JPAUnitTest : Started JPAUnitTest in 3.597 seconds (JVM running for 4.482)
2020-07-30 17:23:16.927 INFO 15464 --- [ main] o.s.t.c.transaction.TransactionContext : Began transaction (1) for test context [DefaultTestContext@19e4fcac testClass = JPAUnitTest, testInstance = com.notyfyd.JPAUnitTest@68b32e3e, testMethod = should_save_an_user@JPAUnitTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@52c3cb31 testClass = JPAUnitTest, locations = '{}', classes = '{class com.notyfyd.JpaDemoApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@4b79ac84 key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@4681c175, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@6fa51cd4, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@6be968ce, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@b6cf2445, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@3de8f619, org.springframework.boot.test.context.SpringBootTestArgs@1], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@11cadb32]; rollback [true]
Hibernate: insert into t_user (email, first_name, last_name, mobile) values (?, ?, ?, ?)
2020-07-30 17:23:17.345 INFO 15464 --- [ main] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test: [DefaultTestContext@19e4fcac testClass = JPAUnitTest, testInstance = com.notyfyd.JPAUnitTest@68b32e3e, testMethod = should_save_an_user@JPAUnitTest, testException = java.lang.NullPointerException, mergedContextConfiguration = [MergedContextConfiguration@52c3cb31 testClass = JPAUnitTest, locations = '{}', classes = '{class com.notyfyd.JpaDemoApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@4b79ac84 key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@4681c175, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@6fa51cd4, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@6be968ce, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@b6cf2445, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@3de8f619, org.springframework.boot.test.context.SpringBootTestArgs@1], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]
2020-07-30 17:23:17.355 INFO 15464 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2020-07-30 17:23:17.358 INFO 15464 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2020-07-30 17:23:17.373 INFO 15464 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
JunitTest
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit4.SpringRunner;
import com.notyfyd.repository.*;
import com.notyfyd.model.*;
import com.notyfyd.entity.*;
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class JPAUnitTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
RoleRepository roleRepository;
@Autowired
UserRepository userRepository;
@Test
public void should_save_an_user() {
User u1 = new User();
u1.setEmail("email1");
u1.setFirstName("firstName1");
u1.setLastName("lastName1");
u1.setMobile("mobile1");
Role r1 = new Role();
r1.setDescription("description 1");
r1.setName("name1");
User userSaved = userRepository.save(u1);
r1.getUsers().add(userSaved); //here raises the exception
Role roleSaved = roleRepository.save(r1);
u1.getRoles().add(roleSaved);
userSaved = userRepository.save(u1);
assertNotNull(userSaved);
}
}
实体:
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "t_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
@ManyToMany(targetEntity = User.class, mappedBy = "roles", cascade = {CascadeType.PERSIST, CascadeType.DETACH,CascadeType.MERGE,CascadeType.REFRESH})
private List<User> users;
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
和
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private String mobile;
@Column(unique = true)
private String email;
@ManyToMany(targetEntity = Role.class, cascade = {CascadeType.PERSIST, CascadeType.DETACH,CascadeType.MERGE,CascadeType.REFRESH} )
private List<Role> roles;
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Pom
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<!-- version>2.2.5.RELEASE</version-->
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.notyfyd</groupId>
<artifactId>jpa-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>jpa-demo</name>
<description>Demo project for JPA</description>
<properties>
<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-web</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<!-- exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions-->
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
型号:
import java.util.List;
public class RoleModel {
private String name;
private String description;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
和
import com.notyfyd.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
public class UserModel {
private String firstName;
private String lastName;
private String mobile;
private String email;
private List<RoleModel> roles;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public List<RoleModel> getRoles() {
return roles;
}
public void setRoles(List<RoleModel> roles) {
this.roles = roles;
}
}
- 您的
role
实体r1
未通过休眠获取,并且还不是managed
实体,因此r1.getUsers()
是null
。当您调用r1.getUsers()
时,Hibernate 对r1
一无所知。因此,为了防止这些问题,请像下面这样修改Role
实体并使用new ArrayList<>();
对其进行初始化
@Entity
@Table(name = "t_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
@ManyToMany(...
private List<User> users = new ArrayList<>();
...
}