【发布时间】:2023-03-17 03:04:01
【问题描述】:
我在尝试持久化带注释的对象时遇到了这个奇怪的错误。
这是我的代码:
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);
}
}
}
我知道,如果您尝试保留现有对象,您会得到该异常,但我会在调用 persist 方法之前检查这一点。此外,我在我的日志文件和我的 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)相关联。 所以我现在知道,由于许多持续存在而没有刷新我在一个事务中进行,我得到了错误,但我不知道为什么它说重复键。
【问题讨论】:
-
异常的堆栈跟踪是什么?你能展示你是如何映射实体的吗?
-
请注意 Usuario Tweet 关系看起来一方面是 OneToMany,另一方面是 ManyToMany。问题:似乎没有反向关系 Tweet HashTag,对吧?
-
设置 EclipseLink 日志记录以便记录参数并且您可能能够跟踪异常的原因:wiki.eclipse.org/EclipseLink/Examples/JPA/Logging 搜索插入相同 PK 的位置,您会找到问题的原因- 这可能是时间问题,因为没有什么能阻止另一个线程在您调用 find 和事务提交之间插入。立即调用 flush 将最小化另一个线程可以插入具有该标记值的对象的窗口。
-
Chris 我刚刚激活了 EclipseLink 日志记录,我会看到它说什么,但我想知道我是否应该避免在一个未提交的事务中保持和更新许多实体,因为我认为它是所有问题的根源。
-
GUIDO,是的,Tweet HashTag 之间没有反向关系,但我在那里收到错误报告。此外,Usuario Tweet 关系看起来一方面是 OneToMany,另一方面是 ManyToMany,因为我希望它们是两个不同的关系。 Usuario 类中定义的 OneToMay 关系用于注册用户的推文,它是单向的,在 Tweet 类上没有反向关系。 Tweet 中的 ManyToMany 关系用于注册用户,并且在中间连接表中提到了他确实出现的推文。
标签: mysql jpa eclipselink