代码之家  ›  专栏  ›  技术社区  ›  Youssef Khloufi

EclipseLink JPA持久化时重复条目

  •  1
  • Youssef Khloufi  · 技术社区  · 10 年前

    我在尝试持久化带注释的对象时遇到了一个奇怪的错误。

    这是我的代码:

    if (hashTags != null && !hashTags.isEmpty()) {
        tweet.setHashTags(new LinkedList<HashTag>());
        for (HashTag hashTag : hashTags) {
            HashTag hashTagEntity = em
                .find(HashTag.class, hashTag.getTag());
            if (hashTagEntity == null) {
                LogWrapper.logger.debug("H: " + hashTag.getTag());
                em.persist(hashTag);
                tweet.getHashTags().add(hashTag);
            } else {
                tweet.getHashTags().add(hashTagEntity);
            }
        }
    }
    

    我知道,如果您尝试持久化现有对象,您会得到该异常,但在调用持久化方法之前,我确实会检查该异常。此外,我在日志文件和MySQL数据库中都找不到导致异常的主键,这让我很困惑,我如何获得一个异常,该异常指示数据库中最初不存在的主键值。

    编辑

    这些是我的注释类,我只显示属性和注释:

    @Entity
    public class Tweet {
    
        @Id
        @Column(name = "ID")
        private long id;
        private Usuario usuario;
        private String texto;
        @Temporal(TemporalType.TIMESTAMP)
        private Date fecha;
        private int numRetweets;
        @ManyToMany(fetch = FetchType.EAGER)
        @JoinTable(name = "TWEET_USUARIO_MENCIONADO", joinColumns = { @JoinColumn(name = "TWEET_ID", referencedColumnName = "ID") }, inverseJoinColumns = { @JoinColumn(name = "USUARIO_ID", referencedColumnName = "ID") })
        private LinkedList<Usuario> usuariosReferenciados;
        @ManyToMany(fetch = FetchType.EAGER)
        @JoinTable(name = "TWEET_HASHTAG", joinColumns = { @JoinColumn(name = "TWEET_ID", referencedColumnName = "ID") }, inverseJoinColumns = { @JoinColumn(name = "HASHTAG_ID", referencedColumnName = "ID") })
        private LinkedList<HashTag> hashTags;
        ...
    
    @Entity
    @Access(AccessType.FIELD)
    public class Usuario {
    
        @Id
        @Column(name = "ID")
        private long id;
        private String nombreEnPantalla;
        private int numTweets;
        @Temporal(TemporalType.TIMESTAMP)
        private Date fechaRegistro;
        @OneToMany(mappedBy = "usuario", fetch = FetchType.EAGER)
        private LinkedList<Tweet> tweets;
        private int numFollowers;
        private double tweetsPerDay;
        ...
    
    @Entity
    public class HashTag {
    
        @Id
        @Column(name = "ID")
        private String tag;
        ...
    

    这是我遇到问题的整个方法代码:

        public Tweet crearTweet(long idTweet, Date fechaTweet, int numRetweets,
                String textoTweet, long idUsuario, Date fechaRegistroUsuario,
                int followersUsuario, String nombreEnPantallaUsuario,
                int numTweetsUsuario, List<Usuario> usuariosMencionados,
                List<HashTag> hashTags) {
            EntityManager em = emf.createEntityManager();
            em.getTransaction().begin();
            Tweet tweet = new Tweet();
            tweet.setFecha(fechaTweet);
            tweet.setId(idTweet);
            tweet.setNumRetweets(numRetweets);
            tweet.setTexto(textoTweet);
            em.persist(tweet);
            Usuario user = em.find(Usuario.class, idUsuario);
            if (user == null) {
                user = new Usuario();
                user.setFechaRegistro(fechaRegistroUsuario);
                user.setId(idUsuario);
                user.setNombreEnPantalla(nombreEnPantallaUsuario);
                user.setNumFollowers(followersUsuario);
                user.setNumTweets(numTweetsUsuario);
                em.persist(user);
            }
            tweet.setUsuario(user);
            if (usuariosMencionados != null && !usuariosMencionados.isEmpty()) {
                tweet.setUsuariosReferenciados(new LinkedList<Usuario>());
                for (Usuario usuarioMencionado : usuariosMencionados) {
                    Usuario usuarioEntity = em.find(Usuario.class,
                            usuarioMencionado.getId());
                    if (usuarioEntity == null) {
                        LogWrapper.logger.debug("U: " + usuarioMencionado.getId()
                                + " " + usuarioMencionado.getNombreEnPantalla());
                        em.persist(usuarioMencionado);
                        tweet.getUsuariosReferenciados().add(usuarioMencionado);
                    } else {
                        tweet.getUsuariosReferenciados().add(usuarioEntity);
                    }
                }
            }
            if (hashTags != null && !hashTags.isEmpty()) {
                tweet.setHashTags(new LinkedList<HashTag>());
                for (HashTag hashTag : hashTags) {
                    HashTag hashTagEntity = em
                            .find(HashTag.class, hashTag.getTag());
                    if (hashTagEntity == null) {
                        LogWrapper.logger.debug("H: " + hashTag.getTag());
                        em.persist(hashTag);
                        tweet.getHashTags().add(hashTag);
                    } else {
                        tweet.getHashTags().add(hashTagEntity);
                    }
                }
            }
            em.getTransaction().commit();
            em.close();
            return tweet;
        } 
    

    这是整个堆栈跟踪:

    [EL Warning]: 2015-03-17 01:50:48.411--UnitOfWork(841203)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.DatabaseException
    Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '??' for key 'PRIMARY'
    Error Code: 1062
    Call: INSERT INTO HASHTAG (ID) VALUES (?)
        bind => [1 parameter bound]
    Query: InsertObjectQuery(um.es.TFG.modelo.HashTag@7a77e4)
    Exception in thread "main" javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.DatabaseException
    Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '??' for key 'PRIMARY'
    Error Code: 1062
    Call: INSERT INTO HASHTAG (ID) VALUES (?)
        bind => [1 parameter bound]
    Query: InsertObjectQuery(um.es.TFG.modelo.HashTag@7a77e4)
        at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:159)
        at um.es.TFG.dao.jpa.JPATweetDAO.crearTweet(JPATweetDAO.java:76)
        at um.es.TFG.core.TweetProcessorMiner.startProcess(TweetProcessorMiner.java:105)
        at um.es.TFG.main.Main.main(Main.java:24)
    Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.DatabaseException
    Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '??' for key 'PRIMARY'
    Error Code: 1062
    Call: INSERT INTO HASHTAG (ID) VALUES (?)
        bind => [1 parameter bound]
    Query: InsertObjectQuery(um.es.TFG.modelo.HashTag@7a77e4)
        at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:331)
        at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:902)
        at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:964)
        at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:633)
        at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:560)
        at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:2055)
        at org.eclipse.persistence.sessions.server.ClientSession.executeCall(ClientSession.java:306)
        at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:242)
        at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228)
        at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.insertObject(DatasourceCallQueryMechanism.java:377)
        at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:165)
        at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:180)
        at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.insertObjectForWrite(DatabaseQueryMechanism.java:489)
        at org.eclipse.persistence.queries.InsertObjectQuery.executeCommit(InsertObjectQuery.java:80)
        at org.eclipse.persistence.queries.InsertObjectQuery.executeCommitWithChangeSet(InsertObjectQuery.java:90)
        at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:301)
        at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
        at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:904)
        at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:803)
        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:2896)
        at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1857)
        at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1839)
        at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1790)
        at org.eclipse.persistence.internal.sessions.CommitManager.commitNewObjectsForClassWithChangeSet(CommitManager.java:227)
        at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsForClassWithChangeSet(CommitManager.java:194)
        at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:139)
        at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:4260)
        at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1441)
        at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1531)
        at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:278)
        at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1169)
        at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:134)
        ... 3 more
    Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '??' for key 'PRIMARY'
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
        at java.lang.reflect.Constructor.newInstance(Unknown Source)
        at com.mysql.jdbc.Util.handleNewInstance(Util.java:377)
        at com.mysql.jdbc.Util.getInstance(Util.java:360)
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:971)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3887)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3823)
        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2435)
        at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2582)
        at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2530)
        at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1907)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2141)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2077)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2062)
        at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:892)
        ... 35 more
    

    编辑2

    如果我在每次持久化操作后执行em.flush()并在代码中注释以下行,错误将停止弹出:

    tweet.getUsuariosReferenciados().add(usuarioMencionado);
    tweet.getUsuariosReferenciados().add(usuarioEntity);
    tweet.getHashTags().add(hashTag);
    tweet.getHashTags().add(hashTagEntity);
    

    我正在对上面的行进行注释,试图尽可能少地进行操作来查找问题,我已经尝试过不注释这些行,如果这样做,我仍然会收到错误,但正如我所说的,每次坚持后都对它们进行注释和刷新,我的代码运行良好,只是没有将两个实体列表(HashTags和提到的用户)与我的主实体(Tweet)相关联。 所以我现在知道,由于我在一个事务中执行的许多操作都没有刷新,所以我得到了错误,但我不知道为什么会显示重复密钥。

    1 回复  |  直到 7 年前
        1
  •  0
  •   Youssef Khloufi    10 年前

    在深入查看了EclipseLink生成的日志后,我发现我的主要问题是由于不兼容的字符编码。我的源代码使用UTF-8编码文本,我的数据库使用latin1编码。因此,在我的一个表中插入一个带有主键的新行后,该主键无法在拉丁语1中正确表示,它将被插入为???其中每个询问符号都会替换一个字符,并且密钥的长度与原始单词的长度相匹配,这就是为什么我会出现重复条目异常,因为在插入例如由3个奇怪字符组成的奇怪单词后,例如???当我试图插入另一个由3个不同奇怪字符组成的不同奇怪单词时,会出现重复条目异常。我尝试将数据库中的表配置为使用UTF-8,但我仍然遇到了问题,因为MySQL中的utf8只使用3个字节来表示字符,而我的源代码使用4个字节的UTF-8编码。所以我使用了utf8mb4作为我的表,这不足以解决这个问题,我需要用 character_set_server=utf8mb4 ,重新启动服务器,通过这样做,Connector/J能够正确自动检测UTF-8设置。