我们正在使用JPA构建REST应用程序,但我们无法正常工作。
问题是,在进行更新或添加项目时,jpa选择开始返回更改的多个版本。例如,如果Item包含A并且更改为B然后更改为C,则选择查询将返回第一次运行,B秒,C第三次,第四次等等。有时它不会直接显示但是在浏览器上按住f5以获取GET项目资源然后更改数据总会产生此问题。
我创建了一个干净的示例项目来演示这一点。我们使用Glassfish 4.1.0(因为4.1.1中存在其他问题)。
总共4个类和persistence.xml + pom.xml
的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>test.server</groupId>
<artifactId>testdb</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.3.11.Final</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
</dependencies>
<build>
<finalName>testdb</finalName>
</build>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
</project>
的persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1">
<persistence-unit name="LocalUnit">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>test.server.entities.ItemEntity</class>
<properties>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/testdb"/>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.username" value="root" />
<property name="hibernate.connection.password" value="abc123" />
<property name="hibernate.archive.autodetection" value="class"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
ItemDTO
public class ItemDTO {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
ItemEntity bean(手动添加namedqueries)
@Entity
@NamedQueries({
@NamedQuery(name = "Item.All", query = "SELECT item from ItemEntity item"),
@NamedQuery(name = "Item.Get", query = "SELECT item from ItemEntity item WHERE item.id = :id")
})
@Table(name = "item", schema = "testdb", catalog = "")
public class ItemEntity {
private Integer id;
private String firstName;
private String lastName;
@Id
@Column(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Basic
@Column(name = "firstName")
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
@Basic
@Column(name = "lastName")
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ItemEntity that = (ItemEntity) o;
if (id != null ? !id.equals(that.id) : that.id != null) return false;
if (firstName != null ? !firstName.equals(that.firstName) : that.firstName != null) return false;
if (lastName != null ? !lastName.equals(that.lastName) : that.lastName != null) return false;
return true;
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (firstName != null ? firstName.hashCode() : 0);
result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
return result;
}
}
ItemResources类
@Path("items")
public class ItemResources {
private static EntityManagerFactory emf = null;
public ItemResources() {
if(emf == null) {
emf = Persistence.createEntityManagerFactory("LocalUnit");
}
}
public EntityManager getEntityManager() {
return emf.createEntityManager();
}
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getAllAccounts() {
EntityManager manager = getEntityManager();
Collection<ItemEntity> accounts = null;
try {
accounts = manager.createNamedQuery("Item.All").getResultList();
} catch(Exception e) {
throw new RuntimeException(e);
} finally {
manager.close();
}
return Response.ok().entity(new GenericEntity<Collection<ItemEntity>>(accounts) {}).build();
}
@PUT
@Path("{id}")
@Consumes(MediaType.APPLICATION_JSON)
public Response updateAccount(@PathParam("id") int id, ItemDTO itemDTO) {
EntityManager manager = getEntityManager();
try {
manager.getTransaction().begin();
TypedQuery<ItemEntity> query = manager.createNamedQuery("Item.Get", ItemEntity.class);
query.setParameter("id", id);
ItemEntity account = query.getSingleResult();
account.setFirstName(itemDTO.getFirstName());
account.setLastName(itemDTO.getLastName());
manager.getTransaction().commit();
} catch(Exception e) {
manager.getTransaction().rollback();
throw new RuntimeException(e);
} finally {
manager.close();
}
return Response.ok().entity("OK").build();
}
}
主应用程序类(没有web.xml,空beans.xml)
@ApplicationPath("resources")
public class JAXRSConfiguration extends Application {
}
总结这个不行。在开始时描述的问题发生,这使得它不可能用于任何真实的东西。我们在这里遗漏了什么,这个简单的例子不适用吗?
我们有一个真正的应用程序包含这个问题,我们有时间试图找出它的行为。我们尝试了不同的方法来清除缓存,驱逐工厂,尝试更改连接隔离,在persistence.xml中禁用2ndlevel和查询缓存,但最终仍然遇到相同的问题或交易更严重的数据库问题。尝试运行相同glassfish版本的两个不同服务器。因此,如果我们必须回滚到纯jdbc / sql以使其正常工作,我们就处于这一点。
然而奇怪的是,我还没有能够在intellij中部署时再现这个问题。但是,在我们尝试过的任何服务器上部署.war文件时,问题出现了。但是当正式解复时,似乎使其工作的唯一方法是为每个请求创建一个新的fctory实例,然后在完成时关闭它。这似乎非常浪费。
如果有人能帮助我们解决这个问题,从而避免很多重构,那么我们将永远是伟大的:)我们是否遗漏了一些必要的重要工作?