JPA 多对多关系未插入生成的 table
JPA many to many relation not inserting into generated table
我的项目中有一个多对多关系,虽然我可以在我的两个实体 table 中写入,但关系 table 没有写入任何内容。
下面是我如何使用 JPA 注释声明它:
Professor.java
@Entity
@Table(name = "Professor")
public class Professor implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "idProfessor", nullable = false)
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "ALUNO_PROFESSOR",
joinColumns = @JoinColumn(name = "idProfessor", referencedColumnName = "idProfessor"),
inverseJoinColumns = @JoinColumn(name = "idAluno", referencedColumnName = "idAluno"))
private List<Aluno> alunoList;
// getters and setters
}
Aluno.java
@Entity
@Table(name = "Aluno")
public class Aluno implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "idAluno", nullable = false)
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToMany(mappedBy = "alunoList", fetch = FetchType.EAGER)
private List<Professor> professorList;
// getters and setters
}
这是要插入数据库的服务层:
@Autowired
private AlunoDao alunoDao;
@Autowired
private ProfessorDao professorDao;
@RequestMapping(value = RestUriConstants.SUBMETER, method = RequestMethod.POST)
public @ResponseBody JsonResponse submeter(@RequestBody final Aluno aluno) {
Professor professor = professorDao.find(1);
aluno.setProfessorList(Arrays.asList(professor));
alunoDao.persist(aluno);
...
}
在这种情况下,请考虑我已经为教授创建了一个 ID 为“1”的条目。
正如我所说,它确实在 Aluno 和 Professor table 上写入,但不会将任何内容写入 ALUNO_PROFESSOR table。
我已经看过这三种类似的问题,但其中 none 可以帮助我:
Hibernate and Spring: value of many-to-many not inserted into generated table
JPA many-to-many persist to join table
How to persist @ManyToMany relation - duplicate entry or detached entity
编辑 - 添加更多代码片段
JpaAlunoDao.java
@Repository
public class JpaAlunoDao implements AlunoDao {
@PersistenceContext
private EntityManager em;
@Transactional
public void persist(Aluno aluno) {
em.persist(aluno);
}
}
JpaExercicioDao.java
@Repository
public class JpaExercicioDao implements ExercicioDao {
@PersistenceContext
private EntityManager em;
@Transactional
public void persist(Exercicio exercicio) {
em.persist(exercicio);
}
}
试试这个:
public class Professor {
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "ALUNO_PROFESSOR",
joinColumns = @JoinColumn(name = "idProfessor", referencedColumnName = "idProfessor"),
inverseJoinColumns = @JoinColumn(name = "idAluno", referencedColumnName = "idAluno"))
private List<Aluno> alunoList;
}
public class Aluno {
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "ALUNO_PROFESSOR",
joinColumns = @JoinColumn(name = "idAluno", referencedColumnName = "idAluno"),
inverseJoinColumns = @JoinColumn(name = "idProfessor", referencedColumnName = "idProfessor"))
private List<Professor> professorList;
}
这将确保多对多关系的元数据在两个实体上都可用,并且关系两侧的操作级联到另一侧。
我还建议将 FetchType.EAGER
替换为 FetchType.LAZY
以获得更好的性能,因为这有可能加载非常大的数据集。
我也有同样的问题。我将完整映射声明的位置交换为我们将在其上使用 save() 函数的 class。
你的情况:
public class Aluno {
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "ALUNO_PROFESSOR",
joinColumns = @JoinColumn(name = "idAluno"),
inverseJoinColumns = @JoinColumn(name = "idProfessor")
private List<Professor> professorList;
}
public class Professor {
@ManyToMany(fetch = FetchType.EAGER, mappedBy = "professorList",)
private List<Aluno> alunoList;
}
而且效果很好。
没有必要在两个实体上设置多对多关系。
只需删除session.setFlushMode(FlushMode.MANUAL);
默认情况下 Spring 内的 HibernateTemplate 设置 FlushMode.MANUAL
这是来自 HibernateTemplate
的源代码。
if (session == null) {
session = this.sessionFactory.openSession();
session.setFlushMode(FlushMode.MANUAL);
isNew = true;
}
...
通常,Hibernate 在内存中保存可持久化状态。将这个状态同步到底层DB的过程称为flushing。
当我们使用 save() 方法时,与保存操作相关的数据不会被刷新到 DB,除非并且直到显式调用 flush() 或 commit() 方法.
如果我们使用像 Hibernate 这样的 JPA 实现,那么该特定实现将管理刷新和提交操作。
这里我们要记住的一件事是,如果我们决定自己刷新数据而不提交数据,那么除非在此调用提交,否则外部事务将看不到更改事务或外部事务的隔离级别是READ_UNCOMMITTED。
...
来自 baeldung 的 Spring Data JPA 中 save() 和 saveAndFlush() 的区别:
https://www.baeldung.com/spring-data-jpa-save-saveandflush
employeeRepository.saveAndFlush(new Employee(2L, "Alice"));
或
employeeRepository.save(new Employee(2L, "Alice"));
employeeRepository.flush();
有时问题出在您插入值的方式上。我举例说明。
User user = userFacade.find(1);
Post post = new Post("PRUEBA");
user.addPostCollection(post);
post.addUserCollection(user);
postFacade.create(post);
您必须在 postCollection 中添加 post,在 userCollection 中添加用户。你有两个在两个实体的集合中添加对应的实体。
Class 用户
public void addPostCollection(Post post) {
if(postCollection == null){
postCollection = new ArrayList<Post>();
}
postCollection.add(post);
}
@ManyToMany(mappedBy = "userCollection")
private Collection<Post> postCollection;
Class Post
public void addUserCollection(User user){
if(userCollection == null){
userCollection = new ArrayList<User>();
}
userCollection.add(user);
}
@JoinTable(name = "USER_POST_R", joinColumns = {
@JoinColumn(name = "POSTID", referencedColumnName = "ID")}, inverseJoinColumns = {
@JoinColumn(name = "USERID", referencedColumnName = "ID")})
@ManyToMany
private Collection<User> userCollection;
此外,实例化列表也很重要,例如 userCollection = new ArrayList()。如果不这样做,则不会插入该值。
我的项目中有一个多对多关系,虽然我可以在我的两个实体 table 中写入,但关系 table 没有写入任何内容。
下面是我如何使用 JPA 注释声明它:
Professor.java
@Entity
@Table(name = "Professor")
public class Professor implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "idProfessor", nullable = false)
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "ALUNO_PROFESSOR",
joinColumns = @JoinColumn(name = "idProfessor", referencedColumnName = "idProfessor"),
inverseJoinColumns = @JoinColumn(name = "idAluno", referencedColumnName = "idAluno"))
private List<Aluno> alunoList;
// getters and setters
}
Aluno.java
@Entity
@Table(name = "Aluno")
public class Aluno implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "idAluno", nullable = false)
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToMany(mappedBy = "alunoList", fetch = FetchType.EAGER)
private List<Professor> professorList;
// getters and setters
}
这是要插入数据库的服务层:
@Autowired
private AlunoDao alunoDao;
@Autowired
private ProfessorDao professorDao;
@RequestMapping(value = RestUriConstants.SUBMETER, method = RequestMethod.POST)
public @ResponseBody JsonResponse submeter(@RequestBody final Aluno aluno) {
Professor professor = professorDao.find(1);
aluno.setProfessorList(Arrays.asList(professor));
alunoDao.persist(aluno);
...
}
在这种情况下,请考虑我已经为教授创建了一个 ID 为“1”的条目。
正如我所说,它确实在 Aluno 和 Professor table 上写入,但不会将任何内容写入 ALUNO_PROFESSOR table。
我已经看过这三种类似的问题,但其中 none 可以帮助我:
Hibernate and Spring: value of many-to-many not inserted into generated table
JPA many-to-many persist to join table
How to persist @ManyToMany relation - duplicate entry or detached entity
编辑 - 添加更多代码片段
JpaAlunoDao.java
@Repository
public class JpaAlunoDao implements AlunoDao {
@PersistenceContext
private EntityManager em;
@Transactional
public void persist(Aluno aluno) {
em.persist(aluno);
}
}
JpaExercicioDao.java
@Repository
public class JpaExercicioDao implements ExercicioDao {
@PersistenceContext
private EntityManager em;
@Transactional
public void persist(Exercicio exercicio) {
em.persist(exercicio);
}
}
试试这个:
public class Professor {
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "ALUNO_PROFESSOR",
joinColumns = @JoinColumn(name = "idProfessor", referencedColumnName = "idProfessor"),
inverseJoinColumns = @JoinColumn(name = "idAluno", referencedColumnName = "idAluno"))
private List<Aluno> alunoList;
}
public class Aluno {
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "ALUNO_PROFESSOR",
joinColumns = @JoinColumn(name = "idAluno", referencedColumnName = "idAluno"),
inverseJoinColumns = @JoinColumn(name = "idProfessor", referencedColumnName = "idProfessor"))
private List<Professor> professorList;
}
这将确保多对多关系的元数据在两个实体上都可用,并且关系两侧的操作级联到另一侧。
我还建议将 FetchType.EAGER
替换为 FetchType.LAZY
以获得更好的性能,因为这有可能加载非常大的数据集。
我也有同样的问题。我将完整映射声明的位置交换为我们将在其上使用 save() 函数的 class。 你的情况:
public class Aluno {
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "ALUNO_PROFESSOR",
joinColumns = @JoinColumn(name = "idAluno"),
inverseJoinColumns = @JoinColumn(name = "idProfessor")
private List<Professor> professorList;
}
public class Professor {
@ManyToMany(fetch = FetchType.EAGER, mappedBy = "professorList",)
private List<Aluno> alunoList;
}
而且效果很好。
没有必要在两个实体上设置多对多关系。
只需删除session.setFlushMode(FlushMode.MANUAL);
默认情况下 Spring 内的 HibernateTemplate 设置 FlushMode.MANUAL
这是来自 HibernateTemplate
的源代码。
if (session == null) {
session = this.sessionFactory.openSession();
session.setFlushMode(FlushMode.MANUAL);
isNew = true;
}
...
通常,Hibernate 在内存中保存可持久化状态。将这个状态同步到底层DB的过程称为flushing。
当我们使用 save() 方法时,与保存操作相关的数据不会被刷新到 DB,除非并且直到显式调用 flush() 或 commit() 方法.
如果我们使用像 Hibernate 这样的 JPA 实现,那么该特定实现将管理刷新和提交操作。
这里我们要记住的一件事是,如果我们决定自己刷新数据而不提交数据,那么除非在此调用提交,否则外部事务将看不到更改事务或外部事务的隔离级别是READ_UNCOMMITTED。
...
来自 baeldung 的 Spring Data JPA 中 save() 和 saveAndFlush() 的区别: https://www.baeldung.com/spring-data-jpa-save-saveandflush
employeeRepository.saveAndFlush(new Employee(2L, "Alice"));
或
employeeRepository.save(new Employee(2L, "Alice"));
employeeRepository.flush();
有时问题出在您插入值的方式上。我举例说明。
User user = userFacade.find(1);
Post post = new Post("PRUEBA");
user.addPostCollection(post);
post.addUserCollection(user);
postFacade.create(post);
您必须在 postCollection 中添加 post,在 userCollection 中添加用户。你有两个在两个实体的集合中添加对应的实体。
Class 用户
public void addPostCollection(Post post) {
if(postCollection == null){
postCollection = new ArrayList<Post>();
}
postCollection.add(post);
}
@ManyToMany(mappedBy = "userCollection")
private Collection<Post> postCollection;
Class Post
public void addUserCollection(User user){
if(userCollection == null){
userCollection = new ArrayList<User>();
}
userCollection.add(user);
}
@JoinTable(name = "USER_POST_R", joinColumns = {
@JoinColumn(name = "POSTID", referencedColumnName = "ID")}, inverseJoinColumns = {
@JoinColumn(name = "USERID", referencedColumnName = "ID")})
@ManyToMany
private Collection<User> userCollection;
此外,实例化列表也很重要,例如 userCollection = new ArrayList()。如果不这样做,则不会插入该值。