我是Spring Transaction的新手.我发现的一些事情很奇怪,可能我确实理解了这一点.我希望在方法级别有一个事务处理,并且我在同一个类中有一个调用方法,看起来它不喜欢它,它必须从单独的类调用.我不明白这是怎么可能的.如果有人知道如何解决这个问题,我将不胜感激.我想使用相同的类来调用带注释的事务方法.
这是代码:
public class UserService {
@Transactional
public boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
public boolean addUsers(List<User> users) {
for (User user : users) {
addUser(user.getUserName, user.getPassword);
}
}
}
Run Code Online (Sandbox Code Playgroud) 我在Spring中异步调用方法时遇到麻烦,此时调用程序是一个从外部系统接收通知的嵌入式库.代码如下所示:
@Service
public class DefaultNotificationProcessor implements NotificationProcessor {
private NotificationClient client;
@Override
public void process(Notification notification) {
processAsync(notification);
}
@PostConstruct
public void startClient() {
client = new NotificationClient(this, clientPort);
client.start();
}
@PreDestroy
public void stopClient() {
client.stop();
}
@Async
private void processAsync(Notification notification) {
// Heavy processing
}
}
Run Code Online (Sandbox Code Playgroud)
的NotificationClient内部具有在它接收到来自另一系统的通知的线程.它接受一个NotificationProcessor构造函数,它基本上是将对通知进行实际处理的对象.
在上面的代码中,我将Spring bean作为处理器,并尝试使用@Async注释异步处理通知.但是,似乎通知在与使用的通道相同的线程中处理NotificationClient.实际上,@Async被忽略了.
我在这里错过了什么?
我有一个方法'databaseChanges',它以迭代的方式调用2个操作:A,B.'A'第一,'B'最后."A"和"B"可以是Ç reate,û PDATE d elete功能在我的永久存储,Oracle数据库11g.
比方说,
'A'更新表Users中的记录,属性zip,其中id = 1.
'B'在表爱好中插入记录.
场景:已调用databaseChanges方法,'A'操作并更新记录.'B'运行并尝试插入记录,发生了某种情况,抛出了异常,异常是冒泡到databaseChanges方法.
预期: 'A'和'B'没有任何改变."A"所做的更新将会回滚.'B'没有改变任何东西,嗯......有一个例外.
实际: 'A'更新似乎没有回滚.'B'没有改变任何东西,嗯......有一个例外.
一些代码
如果我有连接,我会做类似的事情:
private void databaseChanges(Connection conn) {
try {
conn.setAutoCommit(false);
A(); //update.
B(); //insert
conn.commit();
} catch (Exception e) {
try {
conn.rollback();
} catch (Exception ei) {
//logs...
}
} finally {
conn.setAutoCommit(true);
}
}
Run Code Online (Sandbox Code Playgroud)
问题:我没有连接(请参阅带问题的标签)
我试过了:
@Service
public class SomeService implements ISomeService {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Autowired
private NamedParameterJdbcTemplate npjt;
@Transactional
private void databaseChanges() throws Exception {
A(); …Run Code Online (Sandbox Code Playgroud) 我使用带有spring-boot-starter-data-jpa的1.2.0版本创建了一个Spring Boot应用程序,我正在使用MySQL.我正确地在application.properties文件中配置了MySQL属性.
我有一个简单的JPA实体,Spring Data JPA Repository和Service如下:
@Entity
class Person
{
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
//setters & getters
}
@Repository
public interface PersonRepository extends JpaRepository<Person, Integer>{
}
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
class PersonService
{
@Autowired PersonRepository personRepository;
@Transactional
void save(List<Person> persons){
for (Person person : persons) {
if("xxx".equals(person.getName())){
throw new RuntimeException("boooom!!!");
}
personRepository.save(person);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我有以下JUnit测试:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class) …Run Code Online (Sandbox Code Playgroud) 我有以下代码:
@Service
public class MyService implements IMyService {
@Inject
IAnotherService anotherService;
// injects go here
// some code
@Transactional(isolation=Isolation.SERIALIZABLE)
public Result myMethod() {
// stuff done here
return this.myPrivateMethod()
}
private Result myPrivateMethod() {
// stuff done here
// multiple DAO SAVE of anObject
anotherService.processSomething(anObject);
return result;
}
}
@Service
public class AnotherService implements IAnotherService {
// injections here
// other stuff
@Transactional(isolation=SERIALIZABLE)
public Result processSomething(Object anObject) {
// some code here
// multiple dao save
// manipulation of anObject …Run Code Online (Sandbox Code Playgroud) 下面是我正在尝试做的快速概述.我想从一个方法调用将记录推送到数据库中的两个不同的表.如果有任何失败,我希望一切都回滚.因此,如果insertIntoB失败,我希望任何可以提交的内容insertIntoA被回滚.
public class Service {
MyDAO dao;
public void insertRecords(List<Record> records){
for (Record record : records){
insertIntoAAndB(record);
}
}
@Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void insertIntoAAndB(Record record){
insertIntoA(record);
insertIntoB(record);
}
@Transactional(propagation = Propagation.REQUIRED)
public void insertIntoA(Record record){
dao.insertIntoA(record);
}
@Transactional(propagation = Propagation.REQUIRED)
public void insertIntoB(Record record){
dao.insertIntoB(record);
}
public void setMyDAO(final MyDAO dao) {
this.dao = dao;
}
}
Run Code Online (Sandbox Code Playgroud)
MyDAO dao使用mybatis映射到数据库的接口在哪里,并使用Spring注入设置.
现在如果insertIntoB失败,insertIntoA仍然会将所有内容都推送到数据库.我该如何纠正这种行为?
编辑:
我修改了这个类,以便更准确地描述我想要实现的目标.如果我运行insertIntoAAndB直接,回滚工作,如果有任何问题,但如果我叫insertIntoAAndB从 …
我在春天使用程序化事务管理,现在我已经切换到声明式事务管理.
SessionFactory的
<beans:bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<beans:property name="dataSource" ref="dataSource" />
<beans:property name="packagesToScan" value="com.hcentive.cig.domain" />
<beans:property name="hibernateProperties">
<beans:props>
<beans:prop key="hibernate.hbm2ddl.auto">update</beans:prop>
<beans:prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect
</beans:prop>
<beans:prop key="hibernate.show_sql">true</beans:prop>
<beans:prop key="hibernate.current_session_context_class">org.hibernate.context.internal.ThreadLocalSessionContext</beans:prop>
</beans:props>
</beans:property>
</beans:bean>
Run Code Online (Sandbox Code Playgroud)
事务管理
<beans:bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<beans:property name="sessionFactory">
<beans:ref bean="sessionFactory" />
</beans:property>
</beans:bean>
Run Code Online (Sandbox Code Playgroud)
现在如果运行我的代码
@Override
@Transactional
public Request saveRequest(Request request) {
sessionFactory.getCurrentSession().save(request);
return request;
}
Run Code Online (Sandbox Code Playgroud)
如果没有活动事务,我会收到异常保存无效
如果我删除下面的行
<beans:prop key="hibernate.current_session_context_class">org.hibernate.context.internal.ThreadLocalSessionContext</beans:prop>
Run Code Online (Sandbox Code Playgroud)
我明白了
没有配置CurrentSessionContext!
试图收集和理解@Transactional注解的要点,并跨越了一个点。因此,在使用 Transactional 注释时,我们需要记住的主要事项是:
不幸的是,我没有找到这个问题的答案:最好将事务注释放在类还是方法中?我们可以考虑不同的情况,但我最感兴趣的是当我们有几种方法必须有这个注释而有些方法没有时。
另外,也许您想在此列表中添加一些要点,那真的很棒。
来自Spring @Transactional属性是否适用于私有方法?
使用代理时,应仅将@Transactional注释应用于具有公共可见性的方法.如果使用@Transactional注释对带保护的,私有的或包可见的方法进行注释,则不会引发错误,但带注释的方法不会显示已配置的事务设置.
我可以想出排除private和package-private方法的充分理由,但为什么protected方法不会在事务上表现?以下stacktrace显示公共方法的正确行为(通过接口代理调用):
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_51]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281) ~[spring-tx-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at com.sun.proxy.$Proxy145.improveType(Unknown Source) ~[na:na]
Run Code Online (Sandbox Code Playgroud)
当调用"相同"的受保护方法(通过非接口CGLIB代理)时,我们得到以下内容:
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_51]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:653) ~[spring-aop-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at my.company.webservices.facade.EntityFacade$$EnhancerBySpringCGLIB$$fd77735b.findEntity(<generated>) ~[spring-core-4.2.1.RELEASE.jar:na]
Run Code Online (Sandbox Code Playgroud)
这显然是一个设计决定(为什么?),但我认为它显然是一个开发人员错误,它无声地失败.
编辑
当使用接口(接口中只有公共方法)时,这显然不是问题,但由于Spring不一定需要通过CGLIB代理对象的接口,因此调用受保护的@Transactional方法将表现得像公共方法(即通过代理),除了按设计它忽略了事务性.
基本上,我试图获得一个与另一个实体具有LAZY关系的实体。以下是我尝试过的两件事。第一个起作用,第二个不起作用,我不明白为什么。我想要的只是从数据库获取实体。之所以将其放在其他方法中,是因为我不希望第一个使用@Transactional,因为它可能需要一些时间才能执行。请注意,在第一种方法中,我并没有保存甚至访问数据库,我只需要从db中读取一次即可。
方法1(按预期工作):
@Override
@Transactional
public void sendEmailToUser(Long exhibitorId) {
EmailExhibitorTO exhibitorTO = findExhibitorById(exhibitorId);
}
private EmailExhibitorTO findExhibitorById(Long id){
return converter.convert(exhibitorRepository.findById(id), EmailExhibitorTO.class);
}
Run Code Online (Sandbox Code Playgroud)
这里的一切都很好,我也得到了实体和惰性初始化的实体。
方法2(无效):
@Override
public void sendEmailToUser(Long exhibitorId) {
EmailExhibitorTO exhibitorTO = findExhibitorById(exhibitorId);
}
@Transactional
private EmailExhibitorTO findExhibitorById(Long id){
return converter.convert(exhibitorRepository.findById(id), EmailExhibitorTO.class);
Run Code Online (Sandbox Code Playgroud)
但是,这不起作用。错误:
有一个映射异常,但这是因为无法获取惰性实体。
我可能只是愚蠢,但如果有我不明白的地方,请解释。
提前致谢。
java ×9
spring ×9
transactions ×5
spring-boot ×2
aspectj ×1
hibernate ×1
isolation ×1
jdbctemplate ×1
jpa ×1
oracle ×1
spring-aop ×1