如何使用 JDBC 创建事务性 DAO
Posted
技术标签:
【中文标题】如何使用 JDBC 创建事务性 DAO【英文标题】:How to create transactional DAOs using JDBC 【发布时间】: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 中所做的相同操作 【参考方案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();
【讨论】:
以上是关于如何使用 JDBC 创建事务性 DAO的主要内容,如果未能解决你的问题,请参考以下文章