我正在尝试使用Maven构建代码,以使用H2数据库学习Spring,Hibernate。目前,我遇到一些问题,如何正确使用@Transactional批注来自动启动事务并在例如EntityManager.persist成功完成或回滚。
我的测试项目非常简单。 POJO类是Person,包含名字,姓氏和电子邮件地址。服务类PersonSerice是提供CRUD功能以添加,更改,读取和删除人员数据的接口。有一个PersonServiceImpl可以调用DAO类的方法。这是方法PersonDAOImpl :: createPerson的示例代码使用
public void createPerson(Person person) {
entityManager.getTransaction().begin();
entityManager.persist(person);
entityManager.getTransaction().commit();
}
一切正常。有一个Hibernate SQL输出
“休眠:调用hibernate_sequence休眠的下一个值:插进入人(电子邮件,nachname,vorname,id)价值观(?,?,?,?)“
我想摆脱手动调用entityManager.getTransaction()。commit();因此,我尝试在调用DAO方法的ServiceImpl方法上编写@Transactional。
public void createPerson(Person person) {
entityManager.getTransaction().begin();
entityManager.persist(person);
entityManager.getTransaction().commit();
}
现在无法正常工作。我知道了”休眠:调用hibernate_sequence的下一个值”数据库中写入了一些内容,但是如果没有手动提交,我将无法列出所有条目或将其删除。所以我目前不知道出了什么问题以及如何让@Transactional自动执行提交。这里部分EntityManager内容显示在Eclipse调试器中:
entityManager $ Proxy26(id = 33)h ExtendedEntityManagerCreator $ ExtendedEntityManagerInvocationHandler(id = 116)containerManaged假exceptionTranslator null假synchronizedWithTransaction false目标SessionImpl(id = 122)actionQueue ActionQueue(id = 306)...autoJoinTransactions true...
我想我的主要问题可能在xml资源文件中,所以我想在这里显示它们。这是我的Beans.xlm(./src/main/resources/Beans.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd">
<context:component-scan base-package="maven.springhibernateh2.basic"></context:component-scan>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="${db.driverClassName}"></property>
<property name="url" value="${db.url}"></property>
<property name="username" value="${db.username}"></property>
<property name="password" value="${db.password}"></property>
</bean>
<bean
class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations">
<list>
<value>database.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>
<!-- Definition des JpaTransactionManagers -->
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- Acitvation of @Transactional Annotation -->
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager" />
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
<property name="persistenceUnitName" value="roland.egger.maven.springhibernate" />
<property name="dataSource" ref="dataSource" />
</bean>
<context:spring-configured />
<context:annotation-config />
</beans>
一行可能是个问题。 "<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager" />"
由于我的pom中没有Aspectj依赖项。但是添加它们并没有什么改变,我也不知道要使@Transactional正常工作需要什么。
现在是其他文件。
这是我的persistence.xml(./src/main/resources/META-INF/persistence.xml)
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="roland.egger.maven.springhibernate" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>maven.springhibernateh2.basic.Person</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
<property name="hibernate.hbm2ddl.auto" value="create" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
这里是我的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>roland.egger</groupId>
<artifactId>maven.springhibernateh2.basic</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<slf4j.version>1.7.30</slf4j.version>
<spring.version>5.2.5.RELEASE</spring.version>
<hibernate.version>5.4.15.Final</hibernate.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-entitymanager -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate.javax.persistence/hibernate-jpa-2.1-api -->
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.2.Final</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- Fuer den RollingFileAppender -->
<dependency>
<groupId>log4j</groupId>
<artifactId>apache-log4j-extras</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
</project>
这里数据库。属性
db.driverClassName=org.h2.Driver
db.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
db.username=sa
db.password=
这里Person.java
package maven.springhibernateh2.basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="person")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="id")
private int personId;
@Column(name = "vorname")
private String Vorname;
@Column(name = "nachname")
private String Nachname;
@Column(name = "email")
private String Emailadresse;
public int getPersonId() {
return personId;
}
public void setPersonId(int personId) {
this.personId = personId;
}
public String getVorname() {
return Vorname;
}
public void setVorname(String vorname) {
Vorname = vorname;
}
public String getNachname() {
return Nachname;
}
public void setNachname(String nachname) {
Nachname = nachname;
}
public String getEmailadresse() {
return Emailadresse;
}
public void setEmailadresse(String emailadresse) {
Emailadresse = emailadresse;
}
public String toString() {
return "Person [PersonId=" + personId + ", Vorname=" + Vorname + ", Nachname=" + Nachname + ", Emailadresse=" + Emailadresse + "]";
}
}
PersonService.java
package maven.springhibernateh2.basic;
import java.util.List;
public interface PersonService {
public abstract void addPerson(Person person);
public abstract Person fetchPersonById(int personId);
public abstract void deletePersonByID(int personId);
public abstract void updatePersonEmailByID(String newEmail, int personId);
public abstract List<Person> getAllPersonInfo();
}
PersonServiceImpl.java
package maven.springhibernateh2.basic;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component("personService")
public class PersonServiceImpl implements PersonService {
@Autowired
private PersonDAO personDAO;
public void setPersonDAO(PersonDAO personDAO) {
this.personDAO = personDAO;
}
@Transactional
public void addPerson(Person person) {
personDAO.createPerson(person);
}
@Transactional
public Person fetchPersonById(int personId) {
return personDAO.getPersonById(personId);
}
@Transactional
public void deletePersonByID(int personId) {
personDAO.deletePersonByID(personId);
}
@Transactional
public void updatePersonEmailByID(String newEmail, int personId) {
personDAO.updatePersonEmailByID(newEmail, personId);
}
@Transactional
public List<Person> getAllPersonInfo() {
return personDAO.getAllPersonData();
}
}
PersonDAO.java
package maven.springhibernateh2.basic;
import java.util.List;
public interface PersonDAO {
public abstract void createPerson(Person person);
public abstract Person getPersonById(int personId);
public abstract void deletePersonByID(int personId);
public abstract void updatePersonEmailByID(String newEmail, int personId);
public abstract List<Person> getAllPersonData();
}
PersonDAOImpl.java
package maven.springhibernateh2.basic;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.springframework.stereotype.Repository;
@Repository
public class PersonDAOImpl implements PersonDAO {
@PersistenceUnit(name = "roland.egger.maven.springhibernate")
private EntityManagerFactory entityManagerFactory;
private EntityManager entityManager;
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
public EntityManagerFactory getEntityManagerFactory() {
return entityManagerFactory;
}
@PersistenceUnit
public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
this.entityManager = this.entityManagerFactory.createEntityManager();
}
public EntityManager getEntityManager() {
return entityManager;
}
public void createPerson(Person person) {
entityManager.persist(person);
}
public Person getPersonById(int personId) {
Person person = entityManager.find(Person.class, personId);
return person;
}
public void deletePersonByID(int personId) {
Person person = getPersonById(personId);
if (person != null) {
//entityManager.getTransaction().begin();
entityManager.remove(person);
//entityManager.getTransaction().commit();
}
}
public void updatePersonEmailByID(String newEmail, int personId) {
Person person = getPersonById(personId);
if (person != null)
{
entityManager.getTransaction().begin();
person.setEmailadresse(newEmail);
entityManager.getTransaction().commit();
}
}
public List<Person> getAllPersonData() {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Person> cq = cb.createQuery(Person.class);
Root<Person> rootEntry = cq.from(Person.class);
CriteriaQuery<Person> all = cq.select(rootEntry);
TypedQuery<Person> allQuery = entityManager.createQuery(all);
return allQuery.getResultList();
}
}
对不起,我发布了源代码,但我希望它可以帮助其他人了解我在做什么,以及如何解决问题以使事务正常运行而无需手动将其写入代码。
使用@PersistenceUnit时,您需要创建/销毁EntityManager并手动管理事务。如果您想使用spring @Transactional,则需要删除由@PersistenceUnit注释的EntityManagerFactory,而对EntityManager变量使用@PersistenceContext,如下所示。
@PersistenceContext
private EntityManager entityManager;
原因是,当您使用@PersistenceContext时,您定义了一个容器管理的bean(这里是spring管理的),因此您不需要显式提交/回滚事务,而使用@PersistenceUnit则指定您想处理交易。
更新:
与最新错误有关,该错误提到“没有EntityManager的当前事务可用的实际事务”:
希望这会有所帮助。
我试图回答Ali Gelenkers的建议,但评论简短,我知道,此后我遇到了entityManager的另一个问题。感谢Ali Gelenker,我被告知在我的EntityManagerFactory的PersonDAOImpl类中的@PersistenceUnit及其设置函数会导致问题,因此应该使用@PersistenceContext。这是我的新代码PersonDaoImpl的一部分
@Repository
public class PersonDAOImpl implements PersonDAO {
private EntityManagerFactory entityManagerFactory;
@PersistenceContext
private EntityManager entityManager;
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
public EntityManagerFactory getEntityManagerFactory() {
return entityManagerFactory;
}
public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
this.entityManager = this.entityManagerFactory.createEntityManager();
}
public EntityManager getEntityManager() {
return entityManager;
}
public void createPerson(Person person) {
entityManager.persist(person);
}
...
现在既不调用setter setEntityManagerFactory,也不调用setter setEntityManager。在调用entityManager.persist(person)期间,createPerson方法中会发生问题。EntityManager调用引发以下异常:
" javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call "
在此异常之前,entityManager在Eclipse的调试器中显示以下内容::
entityManager $Proxy26 (id=40)
h SharedEntityManagerCreator$SharedEntityManagerInvocationHandler (id=47)
logger LogAdapter$Slf4jLocationAwareLog (id=51)
properties null
proxyClassLoader Launcher$AppClassLoader (id=55)
synchronizedWithTransaction true
targetFactory $Proxy23 (id=62)
完整的控制台输出是:
INFO | 2020-05-09 22:44:44,953 | | | main | maven.springhibernateh2.basic.CRUDTest - Programmanfang...
INFO | 2020-05-09 22:44:45,486 | | | main | org.hibernate.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: roland.egger.maven.springhibernate]
INFO | 2020-05-09 22:44:45,532 | | | main | org.hibernate.Version - HHH000412: Hibernate ORM core version 5.4.15.Final
INFO | 2020-05-09 22:44:45,657 | | | main | org.hibernate.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
INFO | 2020-05-09 22:44:46,193 | | | main | org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
Hibernate:
drop table if exists person CASCADE
Hibernate:
drop sequence if exists hibernate_sequence
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate:
create table person (
id integer not null,
email varchar(255),
nachname varchar(255),
vorname varchar(255),
primary key (id)
)
INFO | 2020-05-09 22:44:46,877 | | | main | org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
INFO | 2020-05-09 22:44:46,884 | | | main | org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'roland.egger.maven.springhibernate'
ERROR | 2020-05-09 22:44:46,987 | | | main | maven.springhibernateh2.basic.CRUDTest - javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call
INFO | 2020-05-09 22:44:46,987 | | | main | maven.springhibernateh2.basic.CRUDTest - Programmende...
INFO | 2020-05-09 22:44:46,988 | | | main | org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'roland.egger.maven.springhibernate'
要使entityManager可用于当前线程需要什么?
更新:多亏了Ali Gelenkers的最新建议,我才开始工作:)对于我的测试项目,我选择了没有Aspectj的最简单解决方案。这里是我的Beans.xml的更改部分:...
<!-- <tx:annotation-driven mode="aspectj" transaction-manager="transactionManager" /> -->
<tx:annotation-driven transaction-manager="transactionManager" />
...
<aop:config proxy-target-class="true"/>
现在,无需手动交易调用,一切正常。非常感谢你:)