【发布时间】:2023-03-31 06:19:01
【问题描述】:
我有一个基于 Spring 的应用程序,它有一个后台轮询服务在一个单独的线程中运行,以更新数据库中的数据状态 (EmployeeStatusPollService)。我使用 JPA(Hibernate 供应商)作为存储库。我在两种解决方案中实现了这项服务,但只有一种解决方案有效。以下是两种解决方案。
方案一:其他服务类中的事务方法checkAndUpdateStatus,轮询服务调用它
@Service
public class EmployeeStatusPollService implements Runnable {
@Inject
private EmployeeService employeeService;
private static final int DEFAULT_PAGE_SIZE = 300;
private boolean flag = true;
public EmployeeStatusPollService() {
}
@PostConstruct
public void start() {
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
executor.setConcurrencyLimit(SimpleAsyncTaskExecutor.NO_CONCURRENCY);
executor.execute(this);
}
public void run() {
while(flag) {
try {
int pagenum = 1;
List<Employee> items = null;
do {
items = employeeService.checkAndUpdateStatus(pagenum, DEFAULT_PAGE_SIZE);
} while(items != null && items.size() == DEFAULT_PAGE_SIZE);
} catch(Exception ex) {
}
}
}
}
@Service
public class EmployeeServiceImpl implements EmployeeService {
private static final Logger LOG = LoggerFactory.getLogger(EmployeeServiceImpl.class);
@Inject
private EmployeeRepository employeeRepository;
@Transactional
public List<Employee> checkAndUpdateStatus(int pagenum, int pagesize) throws Exception {
// ....
}
}
解决方案2:事务方法checkAndUpdateStatus在轮询服务类中
@Service
public class EmployeeStatusPollService implements Runnable {
@Inject
private EmployeeRepository employeeRepository;
private static final int DEFAULT_PAGE_SIZE = 300;
private boolean flag = true;
public EmployeeStatusPollService() {
}
@PostConstruct
public void start() {
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
executor.setConcurrencyLimit(SimpleAsyncTaskExecutor.NO_CONCURRENCY);
executor.execute(this);
}
public void run() {
while(flag) {
try {
int pagenum = 1;
List<Employee> items = null;
do {
items = checkAndUpdateStatus(pagenum, DEFAULT_PAGE_SIZE);
} while(items != null && items.size() == DEFAULT_PAGE_SIZE);
} catch(Exception ex) {
}
}
}
@Transactional
public List<Employee> checkAndUpdateStatus(int pagenum, int pagesize) throws Exception {
// ....
}
}
方法checkAndUpdateStatus的详细信息
@Transactional
public List<Employee> checkAndUpdateStatus(int pagenum, int pagesize) throws Exception {
PageRequest page = new PageRequest(pagenum, pagesize);
Page<Employee> pagedItems = employeeRepository.findByStatus(EmployeeStatus.PENDING, page); // Line 1: Query employees
List<Employee> emps = pagedItems.getContent();
List<Long> updatedItems = new ArrayList<>();
int i = 0;
for(Employee emp:emps) {
try {
// ...
emp.setStatus(status); // Line 2: Update employee's status
employeeRepository.save(emp); // Line 3: Save/Update employee
updatedItems.add(emp.getId());
i++;
if(i % 50 == 0) {
employeeRepository.flush(); // Line 4: flush for every 50 employees
}
//....
} catch (Exception ex) {
// handle exception here....
}
}
return emps;
}
配置
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="defaultAutoCommit" value="false" />
</bean>
<bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="jpaDialect" ref="jpaDialect"></property>
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"></property>
<property name="packagesToScan" value="${jpa.packagesToScan}" />
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<prop key="hibernate.connection.autocommit">${hibernate.connection.autocommit}</prop>
<prop key="hibernate.connection.defaultAutoCommit">${hibernate.connection.defaultAutoCommit}</prop>
</props>
</property>
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean>
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
解决方案 2 不起作用,当它在方法 checkAndUpdateStatus 的第 3 行将实体更新/保存到数据库时,我收到错误“持久实体已分离...”。
IMO,解决方案 2 不起作用,因为方法 checkAndUpdateStatus 没有被放入事务上下文中,尽管它由 @Transactional 标记。即使我设置了REQUIRES_NEW,它仍然不起作用。任何人都可以向我解释为什么会发生此错误或向我发送一些有关此的参考文档吗?
【问题讨论】:
标签: java spring spring-transactions