类“LocalCertificate”的属性 [id] 映射到数据库中的主键列。不允许更新

问题描述 投票:0回答:1

我很久以来就面临这个问题,我不知道 java 端有这个问题。我正在处理一个旧的 JPA 项目,而没有 java 和 Postgres SQL 的先验知识。

我有一个实体类,我可以在其中推送到 derby db。但现在我也想将数据复制到 Postgres 中。

实体类:

@Table(name = "certs")
public class LocalCertificate implements Serializable {

    private static final long serialVersionUID = -5003848691574858779L;

    @Expose
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Expose
    @Column(name = "d_id")
    private String d_id;
    
    @Expose
    @Column(name = "certificate", columnDefinition="clob")
    @Convert()
    @Lob
    private X509CertificateHolder certificate;
    
    @Expose
    @Column(name = "revoked")
    public boolean revoked = false;
    
    public byte[] getCertDER() throws IOException {
        return certificate.getEncoded();
    }
    
    
    public long getId() {
        return id.longValue();
    }
    
    public String getDId() {
        return d_id;
    }
    
    public void store() {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("LDB");
        EntityManager locEm = emf.createEntityManager();

        EntityTransaction ta = locEm.getTransaction();
        ta.begin();
        locEm.persist(this);
        ta.commit();
        
        // for Postgres
        EntityManagerFactory eemf = Persistence.createEntityManagerFactory("PU_CSA");
        EntityManager llocEm = eemf.createEntityManager();

        EntityTransaction tta = llocEm.getTransaction();
        tta.begin();
        llocEm.persist(this);
        tta.commit();
    }
    
    public void update() {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("LDB");
        EntityManager locEm = emf.createEntityManager();

        EntityTransaction ta = locEm.getTransaction();
        
        ta.begin();
        locEm.merge(this);
        ta.commit();
        
        // for postgres
        EntityManagerFactory eemf = Persistence.createEntityManagerFactory("PU_CSA");
        EntityManager llocEm = eemf.createEntityManager();

        EntityTransaction tta = llocEm.getTransaction();
        
        tta.begin();
        llocEm.merge(this);
        tta.commit();
    }
    
    public void setD_id(String dId) {
        // TODO Auto-generated method stub
        this.d_id =  dId;
    }


    public void setCertificate(X509CertificateHolder cert) {
        // TODO Auto-generated method stub
        this.certificate = cert;        
    }
}

持久化xml文件:

<persistence-unit name="LDB">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <non-jta-data-source>java:comp/env/jdbc/LDB</non-jta-data-source>
        <class>org.backend.dao.LocalCertificate</class>
        <class>org.backend.certserver.jpa.LocalCertificateAdapter</class>
        
        <properties>
            <!-- EclipseLink should create the database schema automatically -->
            <property name="eclipselink.ddl-generation" value="create-or-extend-tables" />
            <property name="eclipselink.ddl-generation.output-mode" value="database" />
        </properties>
    </persistence-unit>

    <persistence-unit name="PU_CSA">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <non-jta-data-source>java:comp/env/jdbc/CSA</non-jta-data-source>
        <class>org.backend.dao.Device</class>
        <class>org.backend.dao.LocalCertificate</class>
        <class>org.backend.certserver.jpa.LocalCertificateAdapter</class>
        <properties>
            <!-- EclipseLink should create the database schema automatically -->
            <property name="eclipselink.ddl-generation" value="create-or-extend-tables" />
            <property name="eclipselink.ddl-generation.output-mode" value="database" /> 
        </properties>
    </persistence-unit>

对于 d_id 和证书列,我可以看到 NULL 值。如何解决这个问题?

编辑:

在失败的地方添加 servlet 文件

private void processDevCertRequest(HttpServletResponse response, String devId) throws IOException {

        LocalCertificate newCert = new LocalCertificate();
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("LDB");
        EntityManager locEm = emf.createEntityManager();

        EntityTransaction ta = locEm.getTransaction();
        ta.begin();
        locEm.persist(newCert);
        ta.commit();
        
        // postgres
        EntityManagerFactory posemf = Persistence.createEntityManagerFactory("PU_CSA");
        EntityManager posEm = posemf.createEntityManager();
        logger.info(newCert.toString());
        EntityTransaction posta = posEm.getTransaction();
        posta.begin();
        posEm.persist(newCert);
        posta.commit();
        
        BigInteger certID = BigInteger.valueOf(newCert.getId());

        CertificateAuthorithyManager cam = CertificateAuthorithyManager.getInstance();
        KeyPairCert kpc = cam.generateSignedKeyPairCert(devId, certID);

        String pemcert = CAServletHelper.generatePEMBlock(kpc.cert);
        String pemkey = CAServletHelper.generatePEMBlock(kpc.keyPair.getPrivate());
        

        ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
        ZipOutputStream zos = new ZipOutputStream(byteOutput);
        zos.putNextEntry(new ZipEntry(devId + "_cert.pem"));
        zos.write(pemcert.getBytes(), 0, pemcert.length());
        zos.closeEntry();
        zos.putNextEntry(new ZipEntry(devId + "_key.pem"));
        zos.write(pemkey.getBytes(), 0, pemkey.length());
        zos.closeEntry();
        zos.close();

        newCert.setDev_id(devId);
        newCert.setCertificate(kpc.cert);
        ta.begin();
        locEm.merge(newCert);
        ta.commit();            <==== here it is failing
        
        // postgress
        newCert.setDev_id(devId);
        newCert.setCertificate(kpc.cert);
        posta.begin();
        posEm.merge(newCert);
        posta.commit();

        OutputStream resos = response.getOutputStream();
        response.setContentType("application/zip");
        response.setStatus(HttpServletResponse.SC_CREATED);
        response.setHeader("Content-Disposition", "attachment;filename=\"" + devId + ".zip\"");
        resos.write(byteOutput.toByteArray());
        resos.flush();
        byteOutput.close();
    }
java postgresql jpa eclipselink
1个回答
0
投票

为此,您需要创建一个您正在持久化/合并的实例的副本,以便在第二个持久化上下文中使用——更像是:

Static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("LDB");
// for postgres
Static final EntityManagerFactory eemf = Persistence.createEntityManagerFactory("PU_CSA");

public void update(LocalCertificate cert) {
    EntityManager locEm = emf.createEntityManager()
    try {
      EntityTransaction ta = locEm.getTransaction();
    
      ta.begin();
      locEm.merge(cert);
      ta.commit();
    } finally { locEm.close();}

    CopyGroup group = new CopyGroup();
    LocalCertificate copy = (LocalCertificate)em.unwrap( JpaEntityManager.class ).copy( cert, group );
    
    EntityManager llocEm = eemf.createEntityManager();
    try {
      EntityTransaction tta = llocEm.getTransaction();
    
      tta.begin();
      llocEm.merge(copy);
      tta.commit();
    } finally { llocEm.close();}
}

为了持久化一个新的实体,你将不得不做更多的事情,因为你的模型不会像现在这样工作——你可能不能在 derby 和 Postgres 中使用身份管理,否则你的实体将被赋予不同的主键值。为了解决这个问题,最简单的解决方案是自己赋值,比如:

@Table(name = "certs")
public class LocalCertificate implements Serializable {

  @Id
  private String id;
  ..
  
  public void assignId() {
    if (StringUtil.isBlank(id)) {
      id = UUID.randomUUID().toString();
    }
  }
 }

然后您可以使用相同的概念进行持久化,只需在传入的第一个对象上调用 assignId,并在传入第二个持久化单元的副本中也使用该赋值。

© www.soinside.com 2019 - 2024. All rights reserved.