JPA ManyToMany重复键问题

时间:2018-05-30 21:49:54

标签: java spring-mvc jpa orm

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

Relation

电影有多个演员,但演员可以在多部电影中。现在,当我试图添加两部具有共同角色的电影时,问题就出现了。

我们假设我添加了死区,之后我添加了死区2.当我添加第二部电影时,我收到以下错误:

  

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

现在这是有效的,因为jpa正在将actor插入已存在的数据库中。我不知道如何解决这个问题,我从TheMovieDatabase获取了ID,我不希望在我的数据库中有重复的Actors。

MOVIE:

@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:语句已中止,因为它会在唯一或主键约束或由&#39; SQL180530224955890&标识的唯一索引中导致重复键值#39;定义于&#39; ACTOR&#39;。   错误代码:20000   致电:插入ACTOR(ID,MOVIECHARACTER,NAME,PROFILEPICTURE)值(?,?,?,?)       bind =&gt; [4个参数绑定]   查询:具有根本原因的InsertObjectQuery(model.movi​​e.actor.Actor@d681)]    错误23505:语句已中止,因为它会在唯一或主键约束或由&#39; SQL180530224955890&#39;标识的唯一索引中导致重复键值。定义于&#39; ACTOR&#39;。       at org.apache.derby.client.am.ClientStatement.completeExecute(Unknown Source)       在org.apache.derby.client.net.NetStatementReply.parseEXCSQLSTTreply(未知来源)       在org.apache.derby.client.net.NetStatementReply.readExecute(未知来源)       在org.apache.derby.client.net.StatementReply.readExecute(未知来源)       at org.apache.derby.client.net.NetPreparedStatement.readExecute_(未知来源)       at org.apache.derby.client.am.ClientPreparedStatement.readExecute(Unknown Source)       at org.apache.derby.client.am.ClientPreparedStatement.flowExecute(Unknown Source)       在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)       at 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)       at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:165)       at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:180)       在org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.insertObjectForWrite(DatabaseQueryMechanism.java:502)       at 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)       at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)       在org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:911)       at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:810)       at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108)       at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85)       at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2979)       at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1892)       at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1874)       at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1824)       at org.eclipse.persistence.internal.sessions.CommitManager.commitNewObjectsForClassWithChangeSet(CommitManager.java:227)       at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsForClassWithChangeSet(CommitManager.java:194)       在org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:139)       at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:4384)       at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1491)       at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1581)       at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:278)       at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1218)       at 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)       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: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)

有人能指出我正确的方向吗? 谢谢!

1 个答案:

答案 0 :(得分:0)

我解决了这个问题:

我使用了merge而不是persist,因为如果实体存在,它将更新它,否则插入它。