MySQLIntegrityConstraintViolationException崩溃了应用程序

时间:2014-11-03 09:42:25

标签: java mysql jpa exception-handling rss

此应用程序RomeCNN使用Rome 1.5.0来迭代CNN新闻源并将URL保存到数据库中。数据库拒绝重复链接。

MySQLIntegrityConstraintViolationException类型的例外是可以的,这些插入应该被数据库拒绝。也许有更好的方法来避免重复,但现在这已经足够了。 (除非发生其他事情?我在思考"如果链接已经存在,请抓住异常,然后转到下一个"。)

为什么应用程序崩溃?在一定程度的完整性约束违规后,连接被破坏了吗?

run:
     [java] [EL Info]: 2014-11-03 01:20:39.432--ServerSession(28432534)--EclipseLink, version: Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd
     [java] [EL Info]: connection: 2014-11-03 01:20:40.173--ServerSession(28432534)--file:/home/thufir/NetBeansProjects/RomeCNN/build/classes/_RomeReaderPU login successful
     [java] [EL Warning]: 2014-11-03 01:20:40.405--UnitOfWork(6261946)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
     [java] Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'http://rss.cnn.com/~r/rss/cnn_topstories/~3/WD5Aw61It7M/nr-kenya' for key 'UNQ_links_0'
     [java] Error Code: 1062
     [java] Call: INSERT INTO links.links (created, link, status) VALUES (?, ?, ?)
     [java]     bind => [3 parameters bound]
     [java] Query: InsertObjectQuery(romereader.Link[ id=null ])
     [java] Exception in thread "main" javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
     [java] Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'http://rss.cnn.com/~r/rss/cnn_topstories/~3/WD5Aw61It7M/nr-kenya' for key 'UNQ_links_0'
     [java] Error Code: 1062
     [java] Call: INSERT INTO links.links (created, link, status) VALUES (?, ?, ?)
     [java]     bind => [3 parameters bound]
     [java] Query: InsertObjectQuery(romereader.Link[ id=null ])
     [java]     at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:157)
     [java]     at romereader.LinkJpaController.create(LinkJpaController.java:31)
     [java]     at romereader.Main.getLinks(Main.java:41)
     [java]     at romereader.Main.main(Main.java:21)
     [java] Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
     [java] Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'http://rss.cnn.com/~r/rss/cnn_topstories/~3/WD5Aw61It7M/nr-kenya' for key 'UNQ_links_0'
     [java] Error Code: 1062
     [java] Call: INSERT INTO links.links (created, link, status) VALUES (?, ?, ?)
     [java]     bind => [3 parameters bound]
     [java] Query: InsertObjectQuery(romereader.Link[ id=null ])
     [java]     at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:331)
     [java]     at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:900)
     [java]     at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:962)
     [java]     at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:631)
     [java]     at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:558)
     [java]     at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:2002)
     [java]     at org.eclipse.persistence.sessions.server.ClientSession.executeCall(ClientSession.java:298)
     [java]     at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:242)
     [java]     at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228)
     [java]     at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.insertObject(DatasourceCallQueryMechanism.java:377)
     [java]     at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:165)
     [java]     at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:180)
     [java]     at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.insertObjectForWrite(DatabaseQueryMechanism.java:489)
     [java]     at org.eclipse.persistence.queries.InsertObjectQuery.executeCommit(InsertObjectQuery.java:80)
     [java]     at org.eclipse.persistence.queries.InsertObjectQuery.executeCommitWithChangeSet(InsertObjectQuery.java:90)
     [java]     at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:301)
     [java]     at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
     [java]     at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:899)
     [java]     at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:798)
     [java]     at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108)
     [java]     at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85)
     [java]     at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
     [java]     at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1804)
     [java]     at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1786)
     [java]     at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1737)
     [java]     at org.eclipse.persistence.internal.sessions.CommitManager.commitNewObjectsForClassWithChangeSet(CommitManager.java:226)
     [java]     at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:125)
     [java]     at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:4207)
     [java]     at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1441)
     [java]     at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1531)
     [java]     at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:277)
     [java]     at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1169)
     [java]     at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:132)
     [java]     ... 3 more
     [java] Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'http://rss.cnn.com/~r/rss/cnn_topstories/~3/WD5Aw61It7M/nr-kenya' for key 'UNQ_links_0'
     [java]     at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
     [java]     at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
     [java]     at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
     [java]     at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
     [java]     at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
     [java]     at com.mysql.jdbc.Util.getInstance(Util.java:386)
     [java]     at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1040)
     [java]     at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4120)
     [java]     at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4052)
     [java]     at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2503)
     [java]     at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2664)
     [java]     at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2794)
     [java]     at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155)
     [java]     at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2458)
     [java]     at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2375)
     [java]     at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2359)
     [java]     at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:890)
     [java]     ... 34 more
     [java] Java Result: 1

BUILD SUCCESSFUL
Total time: 6 seconds
thufir@dur:~/NetBeansProjects/RomeCNN$ 
thufir@dur:~/NetBeansProjects/RomeCNN$ 

JpaController:

package romereader;

import java.io.Serializable;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Query;
import javax.persistence.EntityNotFoundException;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import romereader.exceptions.NonexistentEntityException;


public class LinkJpaController implements Serializable {

    public LinkJpaController(EntityManagerFactory emf) {
        this.emf = emf;
    }
    private EntityManagerFactory emf = null;

    public EntityManager getEntityManager() {
        return emf.createEntityManager();
    }

    public void create(Link link) {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            em.persist(link);
            em.getTransaction().commit();
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public void edit(Link link) throws NonexistentEntityException, Exception {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            link = em.merge(link);
            em.getTransaction().commit();
        } catch (Exception ex) {
            String msg = ex.getLocalizedMessage();
            if (msg == null || msg.length() == 0) {
                Integer id = link.getId();
                if (findLink(id) == null) {
                    throw new NonexistentEntityException("The link with id " + id + " no longer exists.");
                }
            }
            throw ex;
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public void destroy(Integer id) throws NonexistentEntityException {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            Link link;
            try {
                link = em.getReference(Link.class, id);
                link.getId();
            } catch (EntityNotFoundException enfe) {
                throw new NonexistentEntityException("The link with id " + id + " no longer exists.", enfe);
            }
            em.remove(link);
            em.getTransaction().commit();
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public List<Link> findLinkEntities() {
        return findLinkEntities(true, -1, -1);
    }

    public List<Link> findLinkEntities(int maxResults, int firstResult) {
        return findLinkEntities(false, maxResults, firstResult);
    }

    private List<Link> findLinkEntities(boolean all, int maxResults, int firstResult) {
        EntityManager em = getEntityManager();
        try {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            cq.select(cq.from(Link.class));
            Query q = em.createQuery(cq);
            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }
            return q.getResultList();
        } finally {
            em.close();
        }
    }

    public Link findLink(Integer id) {
        EntityManager em = getEntityManager();
        try {
            return em.find(Link.class, id);
        } finally {
            em.close();
        }
    }

    public int getLinkCount() {
        EntityManager em = getEntityManager();
        try {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            Root<Link> rt = cq.from(Link.class);
            cq.select(em.getCriteriaBuilder().count(rt));
            Query q = em.createQuery(cq);
            return ((Long) q.getSingleResult()).intValue();
        } finally {
            em.close();
        }
    }

}

虽然对数据库进行了插入操作,但是为什么它显然会对限制违规或特定URL造成阻塞,对我来说并不清楚。

persistence.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
  <persistence-unit name="RomeReaderPU" transaction-type="RESOURCE_LOCAL">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <class>romereader.Links</class>
    <class>romereader.Link</class>
    <properties>
      <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/links?zeroDateTimeBehavior=convertToNull"/>
      <property name="javax.persistence.jdbc.password" value="password"/>
      <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
      <property name="javax.persistence.jdbc.user" value="jdbc"/>
    </properties>
  </persistence-unit>
</persistence>

2 个答案:

答案 0 :(得分:1)

最简单的方法是首先检查数据库中是否存在链接,然后更新/跳过或插入新项。基本上是这样的:

em = getEntityManager();
Link existing = em.find(Link.class, link.getLink());
if (existing == null) {
    em.getTransaction().begin();
    em.persist(link);
    em.getTransaction().commit();
}

为了使这更加健壮,我会使用SyndEntry.getUri()值作为唯一标识符。我现在无法检查,但我确定它会映射到项目的guid元素:

<item>
<title>Protectionist wins Melbourne Cup</title>
<guid>http://edition.cnn.com/2014/11/04/asia/gallery/melbourne-cup/index.html</guid>
<link>http://edition.cnn.com/2014/11/04/asia/gallery/melbourne-cup/index.html?eref=edition</link>
<description>Australia's Melbourne Cup</description>
<pubDate>Tue, 04 Nov 2014 01:52:42 EST</pubDate>
</item>

如果您选择更新元素,则可以使用pubDate值来确定自上次保存以来该项目是否已更新。

RSS Duplicate Detection是关于此主题的有趣博客文章(遗憾的是目前只能通过archive.org找到。)

如果您阅读RSS 2.0 specification,则会发现<link>可以省略<item>,因此也可以记住这一点。

答案 1 :(得分:0)

我更新了create方法:

public void create(Link link) {
    EntityManager em = null;
    try {
        em = getEntityManager();
        em.getTransaction().begin();
        em.persist(link);
        em.getTransaction().commit();
    } catch (Exception e) {
        log.fine(e.toString());
    } finally {
        if (em != null) {
            em.close();
        }
    }
}

这似乎很荒谬。但是,由于它现在运行而没有崩溃,我想这是一个解决方案。欢迎提供更好的解决方案。也许一般方法存在缺陷。