【问题标题】:JPA ManyToMany duplicate key issueJPA ManyToMany 重复键问题
【发布时间】:2018-11-09 20:20:34
【问题描述】:

我有以下问题。我有 2 个实体,电影和演员。

Relation

一部电影有多个演员,但一个演员可以出演多部电影。现在,当我尝试添加 2 部具有共同演员的电影时,就会出现问题。

假设我添加了 deadpool,然后我添加了 deadpool 2。当我添加第二部电影时,我收到以下错误:

语句被中止,因为它会导致唯一或主键约束中的重复键值

现在这是有效的,因为 jpa 正在将一个参与者插入到已经存在的数据库中。我不知道如何解决这个问题,我从 TheMovieDatabase 获取 id,我不想在我的数据库中有重复的 Actor。

电影:

@Entity(name = "Movie")
public class Movie {

//instance variables
@Positive
@Id
private long tmdbId;

...

@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(name = "movie_actor",
        joinColumns = @JoinColumn(name = "movie_tmdbid"),
        inverseJoinColumns = @JoinColumn(name = "actor_id")
)
private Set<Actor> actors = new HashSet<>();

...

演员:

@Entity(name = "Actor")
public class Actor {

@Id
private long id;

...

@ManyToMany(mappedBy = "actors")
private Set<Movie> movies = new HashSet<>();

Jpa 添加方法

    public void addMovie(Movie movie) {
    try {
        //open connection
        openConnection();

        //check
        if (movie == null) {
            throw new DatabaseException("You cannot add an empty movie");
        }

        //contains only looks in the current persistence context
        if (entityManager.find(Movie.class,movie.getTmdbId()) != null) {
            throw new MovieAlreadyExistsException("This movie already exists!");
        }

        //begin transaction
        entityManager.getTransaction().begin();

        //add movie & commit
        entityManager.persist(movie);
        entityManager.getTransaction().commit();

    }catch (MovieAlreadyExistsException error){
        throw new MovieAlreadyExistsException(error.getMessage(),error);
    } catch (Exception error) {
        throw new DatabaseException(error.getMessage(), error);
    } finally {
        closeConnection();
    }
}

设置演员的方法:它将从 TheMovieDatabase 中获取演员,而 addMovie 是来自 jpa 在外观后面的演员。

//add a movie
@PostMapping(value = "/add")
public String saveMovie(@ModelAttribute("movie") @Valid Movie movie, BindingResult result) {
    ...

    try{
            //set actors
        Set<Actor> actors = new HashSet<>(mediaService.getMovieActors(movie.getTmdbId()));
        movie.setActors(actors);

        //add movie with actors
        movieService.addMovie(movie);

堆栈跟踪:

内部异常:org.apache.derby.shared.common.error.DerbySQLIntegrityConstraintViolationException:语句被中止,因为它会导致唯一或主键约束或唯一索引中的重复键值由定义的“SQL180530224955890”标识'演员'。 错误代码:20000 呼叫:插入演员(ID、MOVIECHARACTER、姓名、个人资料)值(?、?、?、?) bind => [4个参数绑定] 查询:InsertObjectQuery(model.movi​​e.actor.Actor@d681)] 有根本原因 错误 23505:该语句已中止,因为它会导致在“ACTOR”上定义的“SQL180530224955890”标识的唯一或主键约束或唯一索引中出现重复键值。 在 org.apache.derby.client.am.ClientStatement.completeExecute(未知来源) 在 org.apache.derby.client.net.NetStatementReply.parseEXCSQLSTTreply(未知来源) 在 org.apache.derby.client.net.NetStatementReply.readExecute(未知来源) 在 org.apache.derby.client.net.StatementReply.readExecute(未知来源) 在 org.apache.derby.client.net.NetPreparedStatement.readExecute_(未知来源) 在 org.apache.derby.client.am.ClientPreparedStatement.readExecute(未知来源) 在 org.apache.derby.client.am.ClientPreparedStatement.flowExecute(未知来源) 在 org.apache.derby.client.am.ClientPreparedStatement.executeUpdateX(未知来源) 在 org.apache.derby.client.am.ClientPreparedStatement.executeUpdate(未知来源) 在 org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:895) 在 org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:967) 在 org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:637) 在 org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:564) 在 org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:2093) 在 org.eclipse.persistence.sessions.server.ClientSession.executeCall(ClientSession.java:309) 在 org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:270) 在 org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:256) 在 org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.insertObject(DatasourceCallQueryMechanism.java:405) 在 org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:165) 在 org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:180) 在 org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.insertObjectForWrite(DatabaseQueryMechanism.java:502) 在 org.eclipse.persistence.queries.InsertObjectQuery.executeCommit(InsertObjectQuery.java:80) 在 org.eclipse.persistence.queries.InsertObjectQuery.executeCommitWithChangeSet(InsertObjectQuery.java:90) 在 org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:314) 在 org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58) 在 org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:911) 在 org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:810) 在 org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108) 在 org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85) 在 org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2979) 在 org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1892) 在 org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1874) 在 org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1824) 在 org.eclipse.persistence.internal.sessions.CommitManager.commitNewObjectsForClassWithChangeSet(CommitManager.java:227) 在 org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsForClassWithChangeSet(CommitManager.java:194) 在 org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:139) 在 org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:4384) 在 org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1491) 在 org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1581) 在 org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:278) 在 org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1218) 在 org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:134) 在 model.db.types.MovieDatabaseJpa.addMovie(MovieDatabaseJpa.java:66) 在 model.MovieService.addMovie(MovieService.java:19) 在 web.controller.MovieController.saveMovie(MovieController.java:72) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.lang.reflect.Method.invoke(Method.java:498) 在 org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209) 在 org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) 在 org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) 在 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:870) 在 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:776) 在 org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) 在 org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991) 在 org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) 在 org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978) 在 org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:881)

谁能指出我正确的方向? 谢谢!

【问题讨论】:

  • 您应该发布异常的完整堆栈跟踪以及将Actors 添加到正在保存的Movie 的代码。
  • @BheshGurung 完成
  • 似乎它正在尝试插入刚刚检索到的Actors。试试这个 - 而不是在 saveMovie 中获取和设置 Actors,而是在 addMovie 中执行此操作,以便检索 Actors 和保存 Movie 发生在同一个 Hibernate 会话/事务中。
  • 此外,您(手动)处理连接/会话/事务的方式,例如在您的addMovie 方法中,是一种非常糟糕的做法,尤其是当您使用像 Spring 这样的框架时。
  • @BheshGurung 该服务正在从 TheMovieDatabase api 检索演员,并且未在 jpa 事务中完成。当 jpa 事务发生时,演员已经设置在电影对象 Connection 中,是的,我知道,但它是针对学校原型的。

标签: java spring-mvc jpa orm


【解决方案1】:

我解决了这个问题:

我使用了合并而不是持久化,因为如果实体存在,它将更新它,否则插入它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-09
    • 1970-01-01
    • 2017-02-02
    • 1970-01-01
    • 2017-12-12
    • 2014-02-21
    • 2022-01-23
    相关资源
    最近更新 更多