【发布时间】:2021-12-20 07:12:44
【问题描述】:
我正在使用 Spring Data Jpa 开发一个 Spring Boot 项目,以便在我的 PostgreSQL 数据库上保存数据。 我发现 Hibernate Many To Many 映射存在一些困难。以下是我的问题的详细信息。在我的项目中,表不是由 Hibernate 创建的,但我使用 Hibernate 定义映射我的表的实体类。
我有这 3 个数据库表:
-
portal_user 表:存储门户的用户:
CREATE TABLE IF NOT EXISTS public.portal_user ( id bigint NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ), first_name character varying(50) COLLATE pg_catalog."default" NOT NULL, middle_name character varying(50) COLLATE pg_catalog."default", surname character varying(50) COLLATE pg_catalog."default" NOT NULL, sex "char" NOT NULL, birthdate date NOT NULL, tex_code character varying(50) COLLATE pg_catalog."default" NOT NULL, e_mail character varying(50) COLLATE pg_catalog."default" NOT NULL, contact_number character varying(50) COLLATE pg_catalog."default" NOT NULL, created_at date NOT NULL, CONSTRAINT user_pkey PRIMARY KEY (id) ) -
user_type 表:定义可能的用户类型(如 ADMIN、SIMPLE_USER 等):
CREATE TABLE IF NOT EXISTS public.user_type ( id bigint NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ), type character varying(50)[] COLLATE pg_catalog."default" NOT NULL, description text COLLATE pg_catalog."default", CONSTRAINT user_type_pkey PRIMARY KEY (id) ) -
portal_user_user_type 表:它是 Many To Many 关联表,将前两个表链接在一起:这是因为特定用户可以有多种用户类型和特定用户type 可以关联到多个用户:
CREATE TABLE IF NOT EXISTS public.portal_user_user_type ( id bigint NOT NULL, portal_user_id_fk bigint NOT NULL, user_type_id_fk bigint NOT NULL, CONSTRAINT portal_user_user_type_pkey PRIMARY KEY (id), CONSTRAINT portal_user_user_type_to_portal_user FOREIGN KEY (portal_user_id_fk) REFERENCES public.portal_user (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT portal_user_user_type_to_user_type FOREIGN KEY (user_type_id_fk) REFERENCES public.user_type (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION )
好的,那么我已经将前面的 3 个类映射到了以下实体类:
User 类映射 portal_user 表:
@Entity
@Table(name = "portal_user")
@Data
public class User implements Serializable {
private static final long serialVersionUID = 5062673109048808267L;
@Id
@Column(name = "id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
@Column(name = "first_name")
private String firstName;
@Column(name = "middle_name")
private String middleName;
@Column(name = "surname")
private String surname;
@Column(name = "sex")
private char sex;
@Column(name = "birthdate")
private Date birthdate;
@Column(name = "tex_code")
private String taxCode;
@Column(name = "e_mail")
private String eMail;
@Column(name = "contact_number")
private String contactNumber;
@Temporal(TemporalType.DATE)
@Column(name = "created_at")
private Date createdAt;
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true)
@JsonManagedReference
private Set<Address> addressesList = new HashSet<>();
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
@JsonManagedReference
private Set<User_UserType> userToUserTypeAssociation = new HashSet<>();
public User(String firstName, String middleName, String surname, char sex, Date birthdate, String taxCode,
String eMail, String contactNumber, Date createdAt) {
super();
this.firstName = firstName;
this.middleName = middleName;
this.surname = surname;
this.sex = sex;
this.birthdate = birthdate;
this.taxCode = taxCode;
this.eMail = eMail;
this.contactNumber = contactNumber;
this.createdAt = createdAt;
}
}
注意 1:此 @OneToMany 关系与另一个用例相关并且工作正常(因此它不是此问题的一部分):
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true)
@JsonManagedReference
private Set<Address> addressesList = new HashSet<>();
NOTE-2:User 类包含此 @OneToMany 关系,它实现了我的 Many To Many 的一侧strong> 导致问题的关系:
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
@JsonManagedReference
private Set<User_UserType> userToUserTypeAssociation = new HashSet<>();
User_UserType 类是映射我的多对多关系的实体类。
然后我有 UserType 实体类映射之前的 user_type 数据库表:
@Entity
@Table(name = "user_type")
@Data
public class UserType implements Serializable {
private static final long serialVersionUID = 6904959949570501298L;
@Id
@Column(name = "id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
@Column(name = "name")
private String name;
@Column(name = "description")
private String description;
@OneToMany(mappedBy = "userType")
@JsonManagedReference
private Set<User_UserType> userToUserTypeAssociation = new HashSet<>();
public UserType() {
super();
// TODO Auto-generated constructor stub
}
public UserType(String name, String description) {
super();
this.name = name;
this.description = description;
}
}
注意:这个类包含这个 @OneToMany 关系,代表我正在尝试实现的 多对多 关系的另一端。
@OneToMany(mappedBy = "userType")
@JsonManagedReference
private Set<User_UserType> userToUserTypeAssociation = new HashSet<>();
然后我实现了 User_UserType 实体类映射 portal_user_user_type(我的多对多关联表):
@Entity
@Table(name = "portal_user_user_type")
@Data
public class User_UserType implements Serializable {
private static final long serialVersionUID = -1334879762781878984L;
@Id
@Column(name = "id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
@ManyToOne()
@JoinColumn(name = "portal_user_id_fk")
@JsonBackReference
private User user;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "user_type_id_fk")
@JsonBackReference
private UserType userType;
public User_UserType() {
super();
// TODO Auto-generated constructor stub
}
public User_UserType(User user, UserType userType) {
super();
this.user = user;
this.userType = userType;
}
}
注意:这个类包含两个@ManyToOne()关系,分别指向相关的User和UserType实例。
最后我尝试通过JUnit测试方法来测试它,这个:
@SpringBootTest()
@ContextConfiguration(classes = GetUserWsApplication.class)
@TestMethodOrder(OrderAnnotation.class)
public class UserRepositoryTest {
@Autowired
private UsersRepository userRepository;
@Test
@Order(1)
public void testInsertUser() {
User user = new User("Mario", null, "Rossi", 'M', new Date(), "XXX", "xxx@gmail.com", "329123456", new Date());
Set<Address> addressesList = new HashSet<>();
addressesList.add(new Address("Italy", "RM", "00100", "Via XXX 123", "near YYY", user));
user.setAddressesList(addressesList);
Set<UserType> userTypesList = new HashSet<>();
UserType userType1 = new UserType("ADMIN", "Admin user type !!!");
UserType userType2 = new UserType("USER", "Just a simple user...");
userTypesList.add(userType1);
userTypesList.add(userType2);
User_UserType user_UserType1 = new User_UserType(user, userType1);
User_UserType user_UserType2 = new User_UserType(user, userType2);
Set<User_UserType> user_UserType_List = new HashSet<>();
user_UserType_List.add(user_UserType1);
user_UserType_List.add(user_UserType2);
user.setUserToUserTypeAssociation(user_UserType_List);
userRepository.save(user);
assertTrue(true);
}
}
运行此方法 Spring Boot 启动时没有错误消息。在调试模式下运行它到达 save() 方法执行,我试图保存我的 User 实例(期望也保存定义的关系:所以两个在我的关联表中创建了用户类型和关系)。
这里发生了一些奇怪的事情。 save 方法似乎给了我一个异常(我可以在调试模式下看到它),但我在堆栈跟踪中没有收到错误消息。按照我所获得的打印屏幕:
正如您所见,它进入了荧光笔异常,但在堆栈跟踪中将应用程序运行到最后,没有与此异常相关的错误行。这是我从 Spring Boot 启动到结束的整个堆栈跟踪:
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.6)
2021-11-06 19:09:01.145 INFO 22688 --- [ main] c.e.u.t.R.UserRepositoryTest : Starting UserRepositoryTest using Java 16.0.1 on ubuntu with PID 22688 (started by andrea in /home/andrea/git/get-user-ws)
2021-11-06 19:09:01.148 INFO 22688 --- [ main] c.e.u.t.R.UserRepositoryTest : No active profile set, falling back to default profiles: default
2021-11-06 19:09:01.896 INFO 22688 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2021-11-06 19:09:01.957 INFO 22688 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 53 ms. Found 2 JPA repository interfaces.
2021-11-06 19:09:02.805 INFO 22688 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2021-11-06 19:09:02.871 INFO 22688 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.4.32.Final
2021-11-06 19:09:03.071 INFO 22688 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2021-11-06 19:09:03.211 INFO 22688 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2021-11-06 19:09:03.456 INFO 22688 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2021-11-06 19:09:03.487 INFO 22688 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
2021-11-06 19:09:04.193 INFO 22688 --- [ main] org.hibernate.tuple.PojoInstantiator : HHH000182: No default (no-argument) constructor for class: com.easydefi.users.entity.User (class must be instantiated by Interceptor)
2021-11-06 19:09:04.323 INFO 22688 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2021-11-06 19:09:04.330 INFO 22688 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2021-11-06 19:09:04.553 WARN 22688 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2021-11-06 19:09:05.626 INFO 22688 --- [ main] c.e.u.t.R.UserRepositoryTest : Started UserRepositoryTest in 4.96 seconds (JVM running for 6.304)
2021-11-06 19:11:09.255 INFO 22688 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2021-11-06 19:11:09.257 INFO 22688 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2021-11-06 19:11:09.264 INFO 22688 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
那么我的代码有什么问题?我错过了什么?我该如何尝试修复它?如果抛出此异常,为什么我没有在堆栈跟踪中获取错误行?
【问题讨论】:
-
可以在帖子中添加异常类型和异常消息吗? --- 出于好奇:是否有特定原因将连接表显式建模为实体?
-
@Turing85 正如我在帖子中解释的那样,我首先设计了数据库,然后实现了数据库表,最后将这些表映射到实体类上。问题是我在堆栈跟踪中没有得到任何错误文本......那么我怎样才能给你异常值?
-
预先设计数据库并不意味着我们必须将连接表建模为实体 =) 在
User和UserType中使用@ManyToMany注释Sets(以及一些伴随的注释) ) 应该足够了。 --- 您可以在运行时从调试器中提取变量的值。当应用程序遇到断点时,您应该能够切换到调试视图并查看当前帧中的所有变量。从那里,您可以访问Throwable的确切类型以及所有字段(例如异常消息)。
标签: java spring-boot hibernate spring-data-jpa hibernate-mapping