같은 클래스 내의 메서드에 의한 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);
}
}
}
Spring AOP(동적 객체 및 cglib)의 제한입니다.
트랜잭션을 처리하기 위해 AspectJ를 사용하도록 스프링을 구성하면 코드가 작동합니다.
간단하고 아마도 가장 좋은 대안은 코드를 재팩터화하는 것입니다.예를 들어 사용자를 처리하는 클래스와 각 사용자를 처리하는 클래스가 있습니다.그러면 Spring AOP를 사용한 기본 트랜잭션 처리가 작동합니다.
AspectJ를 사용한 트랜잭션 처리에 대한 구성 힌트
Spring이 트랜잭션에 AspectJ를 사용할 수 있도록 하려면 모드를 AspectJ로 설정해야 합니다.
<tx:annotation-driven mode="aspectj"/>
3.0보다 오래된 버전의 스프링을 사용하는 경우 스프링 구성에도 다음을 추가해야 합니다.
<bean class="org.springframework.transaction.aspectj
.AnnotationTransactionAspect" factory-method="aspectOf">
<property name="transactionManager" ref="transactionManager" />
</bean>
여기서 문제는 Spring의 AAP 프록시는 확장되지 않고 콜을 대행 수신하기 위해 서비스 인스턴스를 랩한다는 것입니다.이것에 의해, 서비스인스턴스내에서 「this」에의 콜은, 그 인스턴스에서 직접 호출되어 래핑 프록시에 의해서 대행 수신할 수 없게 됩니다(프록시는 이러한 콜도 인식하지 않습니다).한 가지 해결 방법이 이미 언급되어 있습니다.또 다른 방법은 Spring이 서비스 자체에 서비스 인스턴스를 삽입하고 삽입된 인스턴스에서 메서드를 호출하는 것입니다.이 인스턴스는 트랜잭션을 처리하는 프록시입니다.단, service bean이 싱글톤이 아닌 경우 이 경우에도 부작용이 발생할 수 있습니다.
<bean id="userService" class="your.package.UserService">
<property name="self" ref="userService" />
...
</bean>
public class UserService {
private UserService self;
public void setSelf(UserService self) {
this.self = self;
}
@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) {
self.addUser(user.getUserName, user.getPassword);
}
}
}
Java 8+에서는 다음 이유로 다른 가능성이 있습니다.
@Service
public class UserService {
@Autowired
private TransactionHandler transactionHandler;
public boolean addUsers(List<User> users) {
for (User user : users) {
transactionHandler.runInTransaction(() -> addUser(user.getUsername, user.getPassword));
}
}
private boolean addUser(String username, String password) {
// TODO call userRepository
}
}
@Service
public class TransactionHandler {
@Transactional(propagation = Propagation.REQUIRED)
public <T> T runInTransaction(Supplier<T> supplier) {
return supplier.get();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public <T> T runInNewTransaction(Supplier<T> supplier) {
return supplier.get();
}
}
이 방법에는 다음과 같은 이점이 있습니다.
개인 방법에 적용할 수 있습니다.따라서 Spring 제한을 충족하기 위해 메서드를 공개함으로써 캡슐화를 해제할 필요가 없습니다.
다른 트랜잭션 전파 내에서 동일한 메서드를 호출할 수 있으며, 적절한 메서드를 선택하는 것은 발신자에게 달려 있습니다.다음 두 줄을 비교합니다.
transactionHandler.runInTransaction(() ->userService.addUser(user.getUserName,user.getPassword));
transactionHandler.runInNewTransaction() ->userService.addUser(user.getUserName,user.getPassword) ;
그것은 명확하기 때문에 더 읽기 쉽다.
Spring 4에서는 자동 전원 공급 가능
@Service
@Transactional
public class UserServiceImpl implements UserService{
@Autowired
private UserRepository repository;
@Autowired
private UserService userService;
@Override
public void update(int id){
repository.findOne(id).setName("ddd");
}
@Override
public void save(Users user) {
repository.save(user);
userService.update(1);
}
}
이것은 셀프 호출을 위한 나의 솔루션입니다.
public class SBMWSBL {
private SBMWSBL self;
@Autowired
private ApplicationContext applicationContext;
@PostConstruct
public void postContruct(){
self = applicationContext.getBean(SBMWSBL.class);
}
// ...
}
같은 클래스 내에서 Bean Factory를 자동 배선하여
getBean(YourClazz.class)
클래스 프록시가 자동으로 수행되고 @Transactional 또는 기타 aop 주석이 고려됩니다.
다음은 같은 클래스 내에서 메서드 호출을 거의 사용하지 않는 소규모 프로젝트에서 수행하는 작업입니다.인코드 문서는 동료에게 이상하게 보일 수 있으므로 강력히 권장합니다.단, 싱글톤으로 동작하고 테스트도 간단하며 Aspect J의 모든 계측을 간단하게 실행할 수 있습니다.다만, 보다 많은 사용량에 대해서는, Espens의 회답에 기재되어 있는 Aspect J 솔루션을 추천합니다.
@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class PersonDao {
private final PersonDao _personDao;
@Autowired
public PersonDao(PersonDao personDao) {
_personDao = personDao;
}
@Transactional
public void addUser(String username, String password) {
// call database layer
}
public void addUsers(List<User> users) {
for (User user : users) {
_personDao.addUser(user.getUserName, user.getPassword);
}
}
}
이 문제는 스프링 로드 클래스와 프록시의 방식과 관련되어 있습니다.다른 클래스에서 내부 메서드/트랜잭션을 쓰거나 다른 클래스로 이동한 후 다시 클래스로 와서 내부 중첩 변환 메서드를 쓸 때까지 작동하지 않습니다.
요약하면 스프링 프록시는 현재 직면한 시나리오를 허용하지 않습니다.당신은 다른 수업에서 두 번째 거래 방법을 써야 합니다.
Aspect J 또는 기타 방법을 사용할 필요가 없습니다.AOP를 사용하는 것만으로 충분합니다.@Transactional을 추가할 수 있습니다.addUsers(List<User> users)현재의 문제를 해결합니다.
public class UserService {
private boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
@Transactional
public boolean addUsers(List<User> users) {
for (User user : users) {
addUser(user.getUserName, user.getPassword);
}
}
}
언급URL : https://stackoverflow.com/questions/3423972/spring-transaction-method-call-by-the-method-within-the-same-class-does-not-wo
'source' 카테고리의 다른 글
| ON DUPLICE KEY를 사용하여 삽입하려는 모든 키를 업데이트하는 방법이 있습니까? (0) | 2023.01.29 |
|---|---|
| Java JTable 설정 컬럼 폭 (0) | 2023.01.29 |
| InnoDB 쓰기 로그 효율이 100% 이상(1953.15%)입니까? (0) | 2023.01.19 |
| vuex에서 '정의되지 않은' 값을 수정하는 방법 (0) | 2023.01.19 |
| DDoS 보호를 활성화하는 방법 (0) | 2023.01.19 |