如何解决AttributeConverter 在 EclipseLink 中不工作,在 Hibernate 中工作正常
我想用转换器尝试一个简单的测试用例。不幸的是,它不适用于 payara 5。它适用于 Wildfly 20.0.1。数据库是H2。
pom.xml
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<modelVersion>4.0.0</modelVersion>
<groupId>fjp</groupId>
<artifactId>converter</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>8.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
持久性.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
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_2.xsd"
>
<persistence-unit name="primary" transaction-type="JTA">
<!--jta-data-source>java:/TestDS</jta-data-source-->
<jta-data-source>jdbc/TestDS</jta-data-source>
<class>fjp.converter.entity.Employee</class>
<class>fjp.converter.entity.converter.StatusConverter</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create" />
<property name="eclipselink.logging.level.sql" value="FINE"/>
<property name="eclipselink.logging.parameters" value="true"/>
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
DAO:
package fjp.converter.dao;
import java.util.List;
import fjp.converter.entity.Employee;
public interface EmployeeDAO {
public Employee find(long i);
public void create(Employee e);
public void delete(Employee e);
public void delete(long i);
public List<Employee> findByStatus(Employee.Status status);
}
DAOImpl
package fjp.converter.dao;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.PersistenceContext;
import javax.persistence.EntityManager;
import fjp.converter.entity.Employee;
@Stateless
public class EmployeeDAOImpl implements EmployeeDAO {
@PersistenceContext
private EntityManager em;
public Employee find(long i) {
return em.find(Employee.class,i);
}
@Override
public void create(Employee e) {
em.persist(e);
}
@Override
public void delete(long i) {
var e = this.find(i);
if(e != null) em.remove(e);
}
@Override
public void delete(Employee e) {
if(e == null) return;
delete(e.getId());
}
@Override
public List<Employee> findByStatus(Employee.Status status) {
return em.createNamedQuery("Employee.findByStatus",Employee.class)
.setParameter("status",status)
.getResultList();
}
}
实体:
package fjp.converter.entity;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.AttributeConverter;
import javax.persistence.Convert;
import javax.persistence.NamedQuery;
import java.io.Serializable;
@NamedQuery(name="Employee.findByStatus",query="select e from Employee e where e.status=:status")
@Entity
public class Employee implements Serializable{
public enum Status {
SENIOR("SENIOR"),JUNIOR("JUNIOR");
private String code;
private Status(String s) {
this.code = s;
}
public String getCode() {
return this.code;
}
}
@Id
private long id;
@Convert(converter = fjp.converter.entity.converter.StatusConverter.class)
private Status status;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Status getStatus() {
return this.status;
}
public void setStatus(Status s) {
this.status = s;
}
@Override
public String toString() {
return String.format("id=%d,status=%s",id,status == null ? null : status.getCode());
}
}
转换器:
package fjp.converter.entity.converter;
import javax.persistence.Converter;
import javax.persistence.AttributeConverter;
import fjp.converter.entity.Employee.Status;
@Converter
public class StatusConverter implements AttributeConverter<Status,String> {
@Override
public String convertToDatabaseColumn(Status e) {
return e == null ? null : e.getCode();
}
@Override
public Status convertToEntityAttribute(String s) {
if(s == null) return null;
switch(s) {
case "SENIOR": return Status.SENIOR;
case "JUNIOR": return Status.JUNIOR;
default: return null;
}
}
}
服务端
package fjp.converter.servlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServlet;
import fjp.converter.dao.EmployeeDAO;
import fjp.converter.entity.Employee;
import fjp.converter.entity.Employee.Status;
import javax.inject.Inject;
@WebServlet("/test")
public class Test extends HttpServlet {
@Inject
private EmployeeDAO dao;
@Override
protected void doGet(HttpServletRequest request,HttpServletResponse response) {
Employee e = new Employee();
long id = 1;
dao.delete(id);
e.setId(id);
e.setStatus(Status.SENIOR);
dao.create(e);
id = 2;
dao.delete(id);
e.setId(id);
e.setStatus(Status.JUNIOR);
dao.create(e);
Status status = Status.SENIOR;
var list = dao.findByStatus(status);
for(var o : list) {
System.out.println(o);
if(o.getStatus() != status) {
System.out.println("ERROR !!!!!");
}
}
status = Status.JUNIOR;
list = dao.findByStatus(status);
for(var o : list) {
System.out.println(o);
if(o.getStatus() != status) {
System.out.println("ERROR !!!!!");
}
}
}
}
第一次询问 servlet 时,您会收到错误消息:
[2021-05-13T19:08:07.512+0200] [Payara 5.2021.3] [PRÉCIS] [] [org.eclipse.persistence.session./file:/home/frederic/payara5/glassfish/domains/domain1/applications/converter-1.0/WEB-INF/classes/_primary.sql] [tid: _ThreadID=76 _ThreadName=http-thread-pool::http-listener-1(5)] [timeMillis: 1620925687512] [levelValue: 500] [[
SELECT ID,STATUS FROM EMPLOYEE WHERE (STATUS = ?)
bind => [SENIOR]]]
[2021-05-13T19:08:07.514+0200] [Payara 5.2021.3] [INFOS] [] [] [tid: _ThreadID=76 _ThreadName=http-thread-pool::http-listener-1(5)] [timeMillis: 1620925687514] [levelValue: 800] [[
id=2,status=JUNIOR]]
[2021-05-13T19:08:07.514+0200] [Payara 5.2021.3] [INFOS] [] [] [tid: _ThreadID=76 _ThreadName=http-thread-pool::http-listener-1(5)] [timeMillis: 1620925687514] [levelValue: 800] [[
ERROR !!!!!]]
如果你刷新页面:它会爆炸!
Local Exception Stack:
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.7.7.payara-p3): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.h2.jdbc.JdbcSQLException: Violation dindex unique ou clé primaire: {0}
Unique index or primary key violation: {0}; SQL statement:
INSERT INTO EMPLOYEE (ID,STATUS) VALUES (?,?) [23505-197]
解决方法
问题是您在以 JPA 规范不允许的方式调用对象后正在编辑对象。这里发生的事情是你首先创建 Employee e 并设置它的 ID 和状态(1,SENIOR),然后在这个实例上调用 persist。
然后更改 e (2,JUNIOR) 上的 id 和 status 值,并再次在同一实例上调用 persist 。实例 E 虽然已经被持久化了,所以这被忽略了。当您查询状态 SENIOR 时,EclipseLink 将查询并找到匹配的行 (1,SENIOR),但是当它进入缓存查看是否已经有数据时,它会找到您的 'e' 实例等等只是返回。您的应用程序记录了一个错误,因为您已将 e 的状态更改为 JUNIOR。
为了证明正在发生的事情 - 尝试列出数据库中的内容。
解决方案只是创建第二个 Employee 实例来表示 (2,JUNIOR) 数据。
JPA 提供程序的一些不同之处在于 EclipseLink 将默认维护一级和二级缓存。这会干扰这种情况,因为您正在以 JPA 规范中不允许的方式修改对象,并且 EclipseLink 比没有缓存时记住数据的时间更长。不允许在 JPA 中修改主键。
,如果我添加到persistence.xml,它会起作用:
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
因此您可以重用 Employe,但要注意二级缓存。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。