Java开发中的JPA乐观锁异常

这篇文章解释了JPA技术及其在Java开发中的用法。 Java开发印度的专家在解释技术的用例-JPA和Hibernate,MySql数据库,Maven。 阅读这篇文章,知道他们想说什么。

技术: JPA代表Java Persistence API,它是Sun Micro Systems的持久性标准。 ORM代表对象关系映射。 JPA是ORM的标准。 如果框架必须说ORM,那么它应该实施JPA给出的所有规范。 JPA只是规范,它需要为CRUD操作提供持久性。 Hibernate,Eclipse链接等是持久性提供程序的示例。

技术: JPA和Hibernate,Maven,MySql数据库

用例:如果您使用JPA,hibernate,eclipse链接来开发Java开发应用程序,则经常会遇到以下异常情况:

javax.persistence.OptimisticLockException: Row was updated or deleted by another 
transaction (or unsaved-value mapping was incorrect).

问题是JPA持久性提供程序正在尝试更新不是最新版本对象的对象。 通常,每当表中的记录更新时,关系数据库都会维护版本。 如果要更新表中的记录,则应从数据库中获取最新版本的记录并进行更新。 在使用ORM时,我们应该获取最后一个版本对象并将其合并。

因此,在本文档中,我们可以看到何时发生此错误以及如何解决该错误。

1.项目结构

jpa-lock-project-structure

项目结构将类似于上面的屏幕截图。

如果查看项目结构,它是一个maven项目pom.xml是它的必需文件。 在这个Maven文件中,我们可以为jpa,hibernate,java,数据库等配置依赖项。我正在使用MySQL数据库进行数据存储。

三个软件包,一个用于实体,一个用于业务逻辑,一个用于应用程序测试。 如果观察到只有一个DTO包,则在多层​​应用程序实体上工作时,实体不会直接暴露给客户端层。 实体将转换为DTO(数据传输对象),该映射将映射到实体。

2.项目依赖

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>naveen.examples</groupId>
	<artifactId>jpa</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>jpa</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.8</java.version>
		<hibernate.version>4.3.6.Final</hibernate.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>

		<!-- JPA -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.version}</version>
		</dependency>

		<!-- For connection pooling -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-c3p0</artifactId>
			<version>${hibernate.version}</version>
		</dependency>

		<!-- Database -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.31</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>

	</dependencies>

</project>

3. JPA配置

由于这是Hibernate添加的hibernate依赖关系,如果是其他ORM,则添加这些依赖关系。

persistence.xml
<persistence 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"
	version="2.1">

	<persistence-unit name="jpa-example" transaction-type="RESOURCE_LOCAL">
		<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
		<properties>
			<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost/employee" />
			<property name="javax.persistence.jdbc.user" value="root" />
			<property name="javax.persistence.jdbc.password" value="root" />
			<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
			<property name="hibernate.show_sql" value="true" />
			<property name="hibernate.format_sql" value="true" />
			<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
			<property name="hibernate.hbm2ddl.auto" value="validate" />
			<!-- Configuring Connection Pool -->
			<property name="hibernate.c3p0.min_size" value="5" />
			<property name="hibernate.c3p0.max_size" value="20" />
			<property name="hibernate.c3p0.timeout" value="500" />
			<property name="hibernate.c3p0.max_statements" value="50" />
			<property name="hibernate.c3p0.idle_test_period" value="2000" />
		</properties>
	</persistence-unit>
</persistence>

这是persistence.xml ,在这里我们需要提及数据库凭证,持久性名称以及实体管理器工厂的所有必需属性。

4.欺诈

这是实体类,它将保留在数据库中。

EmployeeBE.java
// Import statements

@NamedQueries({
	@NamedQuery(name = EmployeeBE.FIND_ALL, query = "SELECT e FROM EmployeeBE e order by e.name "),
})
@Entity
@Table(name = "T_EMP")
public class EmployeeBE implements Serializable{
private static final long serialVersionUID = 1607726899931733607L;
	
	public static final String FIND_ALL = "naveen.examples.jpa.entity.EmployeeBE.find_all";

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;

	@Column(name = "NAME")
	private String name;

	@Column(name = "version_num")
	@Version
	private int version;

//  Getters and setters

}
EmployeeCrud.java
// Interface for CRUD operations.
package naveen.examples.jpa.business;

import java.util.List;

import naveen.examples.jpa.entity.EmployeeBE;

public interface EmployeeCrud {

	List<EmployeeBE> findAllEmployeeBECol();
	
	EmployeeBE saveEmployee(EmployeeBE employeeBE);
	
	EmployeeBE updateEmployee(EmployeeBE employeeBE);
	
	EmployeeBE findById(int id);
	
}
EmployeeCrudImpl.java
// Implementatino class for CRUD operations
public class EmployeeCrudImpl implements EmployeeCrud{

	public EmployeeBE saveEmployee(EmployeeBE employeeBE) {
		EntityManager em = EntityManagerUtil.getEntityManager();
		em.getTransaction().begin();
		em.persist(employeeBE);
		em.getTransaction().commit();
		return employeeBE;
	}

	public EmployeeBE updateEmployee(EmployeeBE employeeBE) {
		EntityManager em = EntityManagerUtil.getEntityManager();
		em.getTransaction().begin();
		em.merge(employeeBE);
		em.getTransaction().commit();
		return employeeBE;
	}

	@SuppressWarnings("unchecked")
	public List<EmployeeBE> findAllEmployeeBECol() {
		EntityManager em = EntityManagerUtil.getEntityManager();
		Query query = em.createNamedQuery(EmployeeBE.FIND_ALL);
		return query.getResultList();
	}


	public EmployeeBE findById(int id) {
		EntityManager em = EntityManagerUtil.getEntityManager();
		return em.find(EmployeeBE.class, id);
	}
EntityManagerUtil.java
// Entity Manager 
public class EntityManagerUtil {

	private static EntityManager  entityManager;

	private EntityManagerUtil() {
	}
	
	public static EntityManager getEntityManager() {
		if(entityManager==null){
			EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("jpa-example");
			return emFactory.createEntityManager();
		}
		return entityManager;
	}
}

如果您看上面的代码片段,我们有

  1. 界面,定义业务方法
  2. InterfaceImpl,业务方法的实现
  3. EntityManagerUtility,获取实体管理器对象。

EntityManagerUtil类中,我们传递了我们在persistence.xml文件中提到的jpa-example参数,因此它将连接到该持久性单元。

5.演示

演示如何实现JPA乐观锁异常。

Application.java
public class Application {

	public static void main(String args[]) {
		
		EmployeeCrud employeeCrud = new EmployeeCrudImpl();
	
		List<EmployeeBE> employeeBEs = employeeCrud.findAllEmployeeBECol();
		if(!employeeBEs.isEmpty()){
			EmployeeBE employeeBE = employeeBEs.get(0);
			employeeBE.setName("Updated");
			employeeCrud.updateEmployee(employeeBE);
			// Here Optimistic lock exception
			employeeCrud.updateEmployee(employeeBE);
		}else{
			EmployeeBE employeeBE = new EmployeeBE();
			employeeBE.setName("Naveen");
			employeeBE = employeeCrud.saveEmployee(employeeBE);
		}
	}
jpa-lock-screen-1

在运行应用程序之前,数据库中没有数据

jpa-lock-screen-2

如果您运行该应用程序

jpa-lock-screen-3
jpa-lock-screen-4

如果您观察到表中的数据为ID 16和版本0。

现在,如果您以调试模式运行该应用程序

jpa-lock-screen-5

如果您观察到第一个更新已完成,但是代码中存在相同的employee对象,则由于第一个更新方法已完成,因此已经提交,因此版本变为1。

jpa锁屏6

如果观察屏幕快照,则该版本变为1,但是该对象是旧版本,即版本0。现在,如果使用旧对象执行第二个更新方法,则将获得乐观锁异常。

jpa-lock-screen-7

如果继续执行,则会导致乐观锁异常。

6.解决方案

要解决此错误,我们有两种方法:

  1. 如果需要将这些值持久保存到新对象并合并,请从数据库中获取最新对象并设置旧对象值。
  2. 对于旧对象,请设置数据库中的最新版本。
jpa锁屏8

我正在采用第二种方法,如果您看到屏幕截图,则将最新版本号设置为旧对象

jpa-lock-screen-9

在控制台中执行后,没有错误:

jpa锁屏10

这是为第二种更新方法生成的SQL查询。 因此没有发生异常。

印度Java开发专家分享了他们对JPA技术及其在Java项目中的使用的最佳知识。 如果您需要更多详细信息,可以询问已经在其项目中应用该技术的开发人员。

结论

通过使用这两种方式,您可以解决此烦人的错误。 因为这是一个非常简单的示例,所以您可以轻松找到它,但是在实时应用程序中,您不会发现它那么简单。 因此,每当您遇到此异常时,以调试模式运行该应用程序,然后转到显示该代码的大概代码段,然后在调试中按F5键,并深入了解您的实体正在更新的其他持久性提供程序的实体管理器实现。 然后根据您的要求遵循这两种技术的eithr4。 继续检查数据库中的版本号。 对于每个更改,它们可能会基于持久性提供程序的实现而增加,也可能不会增加。

参考文献

  1. OptimisticLockException JavaDoc
  2. Java持久性/锁定

翻译自: https://mkyong.com/jpa/jpa-optimistic-lock-exception-in-java-development/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值