【问题标题】:How to create transactional DAOs using JDBC如何使用 JDBC 创建事务性 DAO
【发布时间】:2019-11-16 11:20:10
【问题描述】:

我现在正在使用 JDBC,并希望为数据访问和业务逻辑创建单独的层。我为每个实体创建了几个 DAO,并为我的业务逻辑创建了几个服务。但是我遇到了交易问题。在每个 DAO 中,我都有 CRUD,我在每个操作中打开连接并在关闭它之后。但是如果我需要在事务中使用几个操作,它就行不通了。

所以我为整个 DAO 创建了一个连接,但我需要为 DAO 外部的每个操作打开和关闭连接。

我的 DAO 示例

public class UserDAOImpl implements UserDAO {

    private Connection connection;

    public UserDAO(Connection connection) {
        this.connection = connection;
    }

    // CRUD operations
}

抽象 DAO 工厂

public abstract class DAOFactory {

    public abstract UserDAO getUserDAO();
    public abstract ItemDAO getItemDAO();
    public abstract OrderDAO getOrderDAO();
    public abstract RoleDAO getRoleDAO();

    public static DAOFactory getDAOFactory(Class<? extends DAOFactory> factoryClass) throws IllegalAccessException, InstantiationException {
        return factoryClass.newInstance();
    }
}

MySQL DAO工厂实现示例

public class MySqlDAOFactory extends DAOFactory {

    private UserDAO userDAO;
    private ItemDAO itemDAO;
    private OrderDAO orderDAO;
    private RoleDAO roleDAO;

    @Override
    public UserDAO getUserDAO() {
        if (userDAO == null) {
            userDAO = new UserDAOImpl(getConnection());
        }
        return userDAO;
    }

    @Override
    public ItemDAO getItemDAO() {
        if (itemDAO == null) {
            itemDAO = new ItemDAOImpl(getConnection());
        }
        return itemDAO;
    }

    @Override
    public OrderDAO getOrderDAO() {
        if (orderDAO == null) {
            orderDAO = new OrderDAOImpl(getConnection());
        }
        return orderDAO;
    }

    @Override
    public RoleDAO getRoleDAO() {
        if (roleDAO == null) {
            roleDAO = new RoleDAOImpl(getConnection());
        }
        return roleDAO;
    }

    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() {
        Connection connection = null;
        Context initCtx = null;
        try {
            initCtx = new InitialContext();
            Context envCtx = (Context) initCtx.lookup("java:comp/env");
            DataSource ds = (DataSource) envCtx.lookup("jdbc/mysql");
            connection = ds.getConnection();
        } catch (NamingException | SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }
}

DAO 方法示例

public Optional<User> findById(Long id) {
        User user = null;
        try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM shop.user WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) {
            statement.setLong(1, id);
            ResultSet resultSet = statement.executeQuery();
            resultSet.next();
            user = userMapper.map(resultSet);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return Optional.ofNullable(user);
    }

【问题讨论】:

  • 嗯...我想不出简单的方法,但在我脑海中,您可以使用由 AspectJ 提供支持的拦截器。拦截器将负责管理和提供 DAO 使用的连接实例。
  • @FanaSithole 我不使用 Spring。
  • 是的,我知道,AspectJ 不是 Spring,Spring AOP 相当于 AspectJ
  • @FanaSithole 如果我不使用任何框架会更好。
  • 如果您不想要框架,我建议您对数据访问层使用代理模式来执行您在 Aspect 中所做的相同操作

标签: java jdbc transactions dao


【解决方案1】:

通常,您不必在 DAO 级别处理事务。最好在服务层应用事务管理,因为这样可以控制何时需要/不需要事务的逻辑,还可以控制回滚策略等其他方面。

在您的情况下,您可以查看 JTA 文档。此 API 提供注释和其他工具来轻松管理您的事务:)

【讨论】:

  • 所以我的问题是如何在 DAO 之间传递连接。
  • @JohnDev,让你的DAOFactory 对连接创建一无所知;将该部分移动到服务层 (getConnection()) 并将Connection 对象从服务注入DAOFactory 中,而不是从工厂中。
  • @MickMnemonic 如果我将在我的DAOFactory 中管理连接是否正常?例如,创建用于开始结束交易的方法。
  • @JohnDev 也许可以通过使用 Java 标准 @Inject@Named 注释来尝试使用依赖注入,这些是独立于框架的
【解决方案2】:

现在我已经创建了用于管理所有 DAO 之间的连接的类。我已将抽象类 DAOFactory 更改为接口并创建抽象类 JdbcDaoFactory 并扩展了我的 DaoManager 类。

DAOFactory

public interface DAOFactory {

    UserDAO getUserDAO();
    ItemDAO getItemDAO();
    OrderDAO getOrderDAO();
    RoleDAO getRoleDAO();
}
public abstract class JdbcDaoFactory implements DAOFactory {

    protected Connection connection;

    private UserDAO userDAO;
    private ItemDAO itemDAO;
    private OrderDAO orderDAO;
    private RoleDAO roleDAO;

    protected JdbcDaoFactory(Connection connection) {
        this.connection = connection;
    }

    @Override
    public UserDAO getUserDAO() {
        if (userDAO == null) {
            userDAO = new UserDAOImpl(connection);
        }
        return userDAO;
    }

    @Override
    public ItemDAO getItemDAO() {
        if (itemDAO == null) {
            itemDAO = new ItemDAOImpl(connection);
        }
        return itemDAO;
    }

    @Override
    public OrderDAO getOrderDAO() {
        if (orderDAO == null) {
            orderDAO = new OrderDAOImpl(connection);
        }
        return orderDAO;
    }

    @Override
    public RoleDAO getRoleDAO() {
        if (roleDAO == null) {
            roleDAO = new RoleDAOImpl(connection);
        }
        return roleDAO;
    }
}
public class JdbcDaoManager extends JdbcDaoFactory implements AutoCloseable {

    public JdbcDaoManager() {
        super(DbHelper.getConnection());
    }

    public void beginTransaction() throws SQLException {
        connection.setAutoCommit(false);
    }

    public void commitTransaction() throws SQLException {
        connection.commit();
        connection.setAutoCommit(true);
    }

    public void rollbackTransaction() throws SQLException {
        connection.rollback();
        connection.setAutoCommit(true);
    }

    @Override
    public void close() throws SQLException {
        connection.close();
    }
}

但我仍然不确定这个解决方案有多正确...... 这是它的用法示例。

有交易

try (JdbcDaoManager daoManager = new JdbcDaoManager()) {
    try {
        daoManager.beginTransaction();
        daoManager.getUserDAO().insert(new User("user1", "12345"));
        daoManager.getUserDAO().insert(new User("user2", "12345"));
        daoManager.getUserDAO().insert(new User("user3", "12345"));
        daoManager.commitTransaction();
    } catch (SQLException e) {
        daoManager.rollbackTransaction();
    }
} catch (SQLException e) {
    e.printStackTrace();
}

无交易

try (JdbcDaoManager daoManager = new JdbcDaoManager()) {
   daoManager.getUserDAO().insert(new User("user1", "12345"));
} catch (SQLException e) {
   e.printStackTrace();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-19
    • 2018-01-08
    • 2021-01-07
    • 2017-06-07
    相关资源
    最近更新 更多