我很久以来就面临这个问题,我不知道 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();
}
为此,您需要创建一个您正在持久化/合并的实例的副本,以便在第二个持久化上下文中使用——更像是:
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,并在传入第二个持久化单元的副本中也使用该赋值。