저는 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을 구성하면 코드가 작동합니다.
간단하고 아마도 가장 좋은 대안은 코드를 리팩토링하는 것입니다. 예를 들어 사용자를 처리하는 클래스와 각 사용자를 처리하는 클래스가 있습니다. 그러면 Spring AOP를 사용한 기본 트랜잭션 처리가 작동합니다.
AspectJ로 트랜잭션을 처리하기위한 구성 팁
Spring이 트랜잭션에 AspectJ를 사용하도록하려면 모드를 AspectJ로 설정해야합니다.
<tx:annotation-driven mode="aspectj"/>
3.0 이전 버전에서 Spring을 사용하는 경우이를 Spring 구성에 추가해야합니다.
<bean class="org.springframework.transaction.aspectj
.AnnotationTransactionAspect" factory-method="aspectOf">
<property name="transactionManager" ref="transactionManager" />
</bean>
답변
여기서 문제는 Spring의 AOP 프록시가 확장되지 않고 호출을 가로 채기 위해 서비스 인스턴스를 래핑한다는 것입니다. 이는 서비스 인스턴스 내에서 “this”에 대한 모든 호출이 해당 인스턴스에서 직접 호출되고 래핑 프록시에서 가로 챌 수 없다는 효과가 있습니다 (프록시는 이러한 호출을 인식하지 못함). 한 가지 해결책이 이미 언급되었습니다. 또 다른 멋진 방법은 Spring이 서비스 인스턴스를 서비스 자체에 주입하고 주입 된 인스턴스에서 메서드를 호출하도록하는 것입니다. 이는 트랜잭션을 처리하는 프록시가 될 것입니다. 그러나 서비스 빈이 싱글 톤이 아닌 경우이 역시 나쁜 부작용이있을 수 있습니다.
<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);
}
}
}
답변
Spring 4를 사용하면 Self autowired가 가능합니다.
@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);
}
}
답변
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
}
}
@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();
}
}
이 접근 방식에는 다음과 같은 장점이 있습니다.
1) 비공개 방식에 적용될 수 있습니다 . 따라서 Spring 제한 사항을 충족하기 위해 메서드를 공용으로 만들어 캡슐화를 중단 할 필요가 없습니다.
2) 다른 트랜잭션 전파 내에서 동일한 메서드 를 호출 할 수 있으며 적절한 메서드 를 선택하는 것은 호출자 에게 달려 있습니다. 다음 두 줄을 비교하십시오.
transactionHandler.runInTransaction(() -> userService.addUser(user.getUserName, user.getPassword));
transactionHandler.runInNewTransaction(() -> userService.addUser(user.getUserName, user.getPassword));
3) 명시 적이므로 더 읽기 쉽습니다.
답변
이것은 자기 호출에 대한 내 솔루션입니다 .
public class SBMWSBL {
private SBMWSBL self;
@Autowired
private ApplicationContext applicationContext;
@PostConstruct
public void postContruct(){
self = applicationContext.getBean(SBMWSBL.class);
}
// ...
}
답변
동일한 클래스 내에서 BeanFactory를 자동 연결하고
getBean(YourClazz.class)
자동으로 클래스를 프록시 화하고 @Transactional 또는 기타 aop 주석을 고려합니다.
답변
이 문제는 스프링로드 클래스 및 프록시 방법과 관련이 있습니다. 다른 클래스에서 내부 메서드 / 트랜잭션을 작성하거나 다른 클래스로 이동 한 다음 다시 클래스로 와서 내부 중첩 트랜잭션 메서드를 작성하기 전까지는 작동하지 않습니다.
요약하면 스프링 프록시는 현재 직면하고있는 시나리오를 허용하지 않습니다. 다른 클래스에 두 번째 트랜잭션 메서드를 작성해야합니다.