2

我正在尝试编写一个简单的 webapp,它将电子邮件 ID 作为参数并为具有该 ID 的用户生成一个令牌。我认为我的代码是不言自明的,所以我将它粘贴在这里,这样我就不必详细解释了。

这是我的控制器/servlet 代码

    User user = userManager.getUserByEmailId("xyz@gmail.com");
    if (user == null) {
        //TODO handle this
    }
    if (user.getIssuedTokens() == user.getMaxTokens()) {
        // TODO handle this
    }
    String token = tokenService.createToken();
    user.setToken(token);
    user.setIssuedTokens(user.getIssuedTokens() + 1);
    userManager.updateUser(user);

userManager 和 tokenService 是服务层的实现。

@Service("tokenService")
public class TokenizationServiceImpl implements TokenizationService {

    @Autowired
    private TokenDAO tokenDAO;

    @Transactional
    public String createToken() {
        String uuid = UUID.randomUUID().toString();
        tokenDAO.createToken(uuid);
        return uuid;
    }
}


@Service("usermanager")
public class UserInterfaceImpl implements UserInterface {

    @Autowired
    private UserDAO userDAO;

    @Transactional
    public void createUser() {
        userDAO.createUser();
    }

    public User getUserByEmailId(String emailID) {
        return userDAO.getUserByEmailId(emailID);
    }

    @Transactional
    public void updateUser(User user) {
        userDAO.updateUser(user);

    }

}

我的spring配置是这样的

<tx:annotation-driven />

    <context:component-scan base-package="com.myapp.dao" />
    <context:component-scan base-package="com.myapp.service" />

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">

        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/mydb" />
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>

    <!-- dataSource TransactionManager -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

以下是我的问题:

  1. 将两个服务(usermanager 和 tokenService)注入我的控制器/servlet 然后一个接一个地调用它们是否有意义?或者我应该直接在 TokenServiceImpl 中编写一种方法,直接使用 usermanager 服务或 UserDAO 吗?
  2. 事务属性似乎对我不起作用。当 updateUser 方法失败时,从 createToken() 方法在数据库中创建了一个令牌。我在这里做错了什么?为什么事务没有回滚?
  3. 我一般如何决定我的服务是否应该使用多个 DAO 或其他服务?
4

2 回答 2

2
  1. 是的,控制器中的所有代码都应该在事务服务中。正如你的服务除了委托给 DAO 的方法之外什么都不做。该服务应该包含业务逻辑,并划分事务。

  2. 您的每项服务都是事务性的。因此,在调用 时createToken(),事务开始并在createToken()返回后立即提交。当updateUser()被调用时,一旦updateUser()返回/失败,就会启动另一个事务并提交或回滚。这就是为什么所有控制器代码都应该在单个事务服务中的原因之一。如果是这种情况,两个调用将在一个事务中进行,如果第二个调用失败,整个事务将被回滚,包括令牌生成。

  3. 我的规则是:如果服务只需要从数据库中获取数据,它应该使用 DAO。如果它需要重用已经在另一个服务中定义的业务逻辑,那么它应该委托给那个服务。

于 2013-10-07T21:40:12.643 回答
2

好吧,我对您的第二个问题的想法也可能会回答您的第一个问题。看着你的片段,我可以注意到你正在创建两个不同的事务tokenService.createToken()userManager.updateUser(user)因为你是从方法外部调用它们@Transaction。为了解决此问题,您需要执行以下操作:

public class UserService {

    ...

    @Transactional
    public void assignToken() {
        User user = userManager.getUserByEmailId("xyz@gmail.com");
        if (user == null) {
        //TODO handle this
        }
        if (user.getIssuedTokens() == user.getMaxTokens()) {
        // TODO handle this
        }
        String token = tokenService.createToken();
        user.setToken(token);
        user.setIssuedTokens(user.getIssuedTokens() + 1);
        userManager.updateUser(user);
    }
}

您可以注意到,为了考虑到这种新的事务行为,我创建了一个名为UserService. 即使我不太了解您的应用程序,无法说出最好的方法是什么,但我绝对不会让它进入您的控制器。我认为,您应该将这种行为隔离在:

  • 一个新的业务组件(就像我在这个例子中所做的那样)
  • 或将其封装在您UserManager的例如

现在,由您决定是否值得耦合或UserService为此TokenizationService创建一个新的业务类。阅读您提供的代码,在我看来UserService可能有一个TokenizationService因为令牌不会在不同的上下文中使用。

请让我知道你的意见。

于 2013-10-07T21:45:24.563 回答