Service事务
DAO中不是处理事务的地方,因为DAO中的每个方法都是对数据库的一次操作
在Service中不应该出现Connection,它应该只在DAO中出现,
因为它是JDBC的东西,JDBC的东西是用来连接数据库的
修改JdbcUtils
我们把对事务的开启和关闭放到JdbcUtils中,在Service中调用JdbcUtils的方法来完成事务的处理,
但在Service中就不会再出现Connection这一“禁忌”了。
代码
public class JdbcUtils { // 配置文件的默认配置!要求你必须给出c3p0-config.xml!!! private static ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 它是事务专用连接! private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); /** * 使用连接池返回一个连接对象 * @return * @throws SQLException */ public static Connection getConnection() throws SQLException { Connection con = tl.get(); // 当con不等于null,说明已经调用过beginTransaction(),表示开启了事务! if(con != null) return con; return dataSource.getConnection(); } /** * 返回连接池对象! * @return */ public static DataSource getDataSource() { return dataSource; } /** * 开启事务 * 1. 获取一个Connection,设置它的setAutoComnmit(false) * 2. 还要保证dao中使用的连接是我们刚刚创建的! * -------------- * 1. 创建一个Connection,设置为手动提交 * 2. 把这个Connection给dao用! * 3. 还要让commitTransaction或rollbackTransaction可以获取到! * @throws SQLException */ public static void beginTransaction() throws SQLException { Connection con = tl.get(); if(con != null) throw new SQLException("已经开启了事务,就不要重复开启了!"); /* * 1. 给con赋值! * 2. 给con设置为手动提交! */ con = getConnection();//给con赋值,表示事务已经开始了 con.setAutoCommit(false); tl.set(con);//把当前线程的连接保存起来! } /** * 提交事务 * 1. 获取beginTransaction提供的Connection,然后调用commit方法 * @throws SQLException */ public static void commitTransaction() throws SQLException { Connection con = tl.get();//获取当前线程的专用连接 if(con == null) throw new SQLException("还没有开启事务,不能提交!"); /* * 1. 直接使用con.commit() */ con.commit(); con.close(); // 把它设置为null,表示事务已经结束了!下次再去调用getConnection()返回的就不是con了 tl.remove();//从tl中移除连接 } /** * 提交事务 * 1. 获取beginTransaction提供的Connection,然后调用rollback方法 * @throws SQLException */ public static void rollbackTransaction() throws SQLException { Connection con = tl.get(); if(con == null) throw new SQLException("还没有开启事务,不能回滚!"); /* * 1. 直接使用con.rollback() */ con.rollback(); con.close(); tl.remove(); } /** * 释放连接 * @param connection * @throws SQLException */ public static void releaseConnection(Connection connection) throws SQLException { Connection con = tl.get(); /* * 判断它是不是事务专用,如果是,就不关闭! * 如果不是事务专用,那么就要关闭! */ // 如果con == null,说明现在没有事务,那么connection一定不是事务专用的! if(con == null) connection.close(); // 如果con != null,说明有事务,那么需要判断参数连接是否与con相等,若不等,说明参数连接不是事务专用连接 if(con != connection) connection.close(); }
TxQueryRunner小工具
自动处理连接的关闭,无需再传递连接参数,自动传递
通过继承QueryRunner类 ,同时重写了所有没有接连参数传递的方法(重写都加上了)
代码
/** * 这个类中的方法,自己来处理连接的问题 * 无需外界传递! * 怎么处理的呢? * 通过JdbcUtils.getConnection()得到连接!有可能是事务连接,也可能是普通的连接! * JdbcUtils.releaseConnection()完成对连接的释放!如果是普通连接,关闭之! * @author cxf * */ public class TxQueryRunner extends QueryRunner { @Override public int[] batch(String sql, Object[][] params) throws SQLException { /* * 1. 得到连接 * 2. 执行父类方法,传递连接对象 * 3. 释放连接 * 4. 返回值 */ Connection con = JdbcUtils.getConnection(); int[] result = super.batch(con, sql, params); JdbcUtils.releaseConnection(con); return result; } @Override public <T> T query(String sql, Object param, ResultSetHandler<T> rsh) throws SQLException { Connection con = JdbcUtils.getConnection(); T result = super.query(con, sql, param, rsh); JdbcUtils.releaseConnection(con); return result; } @Override public <T> T query(String sql, Object[] params, ResultSetHandler<T> rsh) throws SQLException { Connection con = JdbcUtils.getConnection(); T result = super.query(con, sql, params, rsh); JdbcUtils.releaseConnection(con); return result; } @Override public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException { Connection con = JdbcUtils.getConnection(); T result = super.query(con, sql, rsh, params); JdbcUtils.releaseConnection(con); return result; } @Override public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException { Connection con = JdbcUtils.getConnection(); T result = super.query(con, sql, rsh); JdbcUtils.releaseConnection(con); return result; } @Override public int update(String sql) throws SQLException { Connection con = JdbcUtils.getConnection(); int result = super.update(con, sql); JdbcUtils.releaseConnection(con); return result; } @Override public int update(String sql, Object param) throws SQLException { Connection con = JdbcUtils.getConnection(); int result = super.update(con, sql, param); JdbcUtils.releaseConnection(con); return result; } @Override public int update(String sql, Object... params) throws SQLException { Connection con = JdbcUtils.getConnection(); int result = super.update(con, sql, params); JdbcUtils.releaseConnection(con); return result; } }
单表的增删改查练习
添加客户分析
add.jsp→CustomerServlet#add()显示添加成功!
代码的实现
DAO#addCustomer(Customer c):
给出sql语句的模板
把参数c转换成一个Object[]
调用QueryRunner的update方法。
Service#addCustomer(Customer c):
直接调用dao的addCustomer(c);
查看客户分析
查看所有用户信息列表
top.jsp→CustomerServlet#findAll()→list.jsp
DAO:
sql = “select * from t_customer”;
qr.query(),需要把结果集映射成List<Customer>,所以使用BeanListHandler
条件查询
query.jsp CustomerServlet#query() list.jsp
修改客户
按id查询流程
编辑流程
删除客户
1.当用户在list.jsp页面中点击“删除”时,通过CustomerServlet的delPre方法来处理:
2.获取cid;
3.通过cid获取Customer对象;
4.把Customer对象保存到request中
5.转发到del.jsp
2.1当用户在del.jsp页面点击删除时,通过Customer的del方法来处理:
2.2获取cid
2.3通过CustomerService来完成删除
3.向页面输出“删除成功”
代码
web层
/** * Web层 * @author cxf * */ public class CustomerServlet extends BaseServlet { private CustomerService customerService = new CustomerService(); /** * 添加客户 */ public String add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* * 1. 封装表单数据到Customer对象 * 2. 补全:cid,使用uuid * 3. 使用service方法完成添加工作 * 4. 向request域中保存成功信息 * 5. 转发到msg.jsp */ Customer c = CommonUtils.toBean(request.getParameterMap(), Customer.class); c.setCid(CommonUtils.uuid()); customerService.add(c); request.setAttribute("msg", "恭喜,添加客户成功!"); return "f:/msg.jsp"; } /** * 查询所有 * @param request * @param response * @return * @throws ServletException * @throws IOException */ public String findAll(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* * 1. 调用service得到所有客户 * 2. 保存到request域 * 3. 转发到list.jsp */ request.setAttribute("cstmList", customerService.findAll()); return "f:/list.jsp"; } /** * 编辑之前的加载工作 * @param request * @param response * @return * @throws ServletException * @throws IOException */ public String preEdit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* * 1. 获取cid * 2. 使用cid来调用service方法,得到Customer对象 * 3. 把Customer保存到request域中 * 4. 转发到edit.jsp显示在表单中 */ String cid = request.getParameter("cid"); Customer cstm = customerService.load(cid); request.setAttribute("cstm", cstm); return "f:/edit.jsp"; } /** * 编辑方法 * @param request * @param response * @return * @throws ServletException * @throws IOException */ public String edit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* * 1. 封装表单数据到Customer对象中 * 2. 调用service方法完成修改 * 3. 保存成功信息到request域 * 4. 转发到msg.jsp显示成功信息 */ // 已经封装了cid到Customer对象中 Customer c = CommonUtils.toBean(request.getParameterMap(), Customer.class); customerService.edit(c); request.setAttribute("msg", "恭喜,编辑客户成功!"); return "f:/msg.jsp"; } public String query(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* * 1. 封装表单数据到Customer对象中,它只有四个属性(cname、gender、cellphone、email) * 它就是一个条件 * 2. 使用Customer调用service方法,得到List<Customer> * 3. 保存到request域中 * 4. 转发到list.jsp */ Customer criteria = CommonUtils.toBean(request.getParameterMap(), Customer.class); List<Customer> cstmList = customerService.query(criteria); request.setAttribute("cstmList", cstmList); return "/list.jsp"; } }
dao层(持久层)
/** * 持久层 * * @author cxf * */ public class CustomerDao { private QueryRunner qr = new TxQueryRunner(); /** * 添加客户 * * @param c */ public void add(Customer c) { try { String sql = "insert into t_customer values(?,?,?,?,?,?,?)"; Object[] params = { c.getCid(), c.getCname(), c.getGender(), c.getBirthday(), c.getCellphone(), c.getEmail(), c.getDescription()}; qr.update(sql, params); } catch(SQLException e) { throw new RuntimeException(e); } } /** * 查询所有 * @return */ public List<Customer> findAll() { try { String sql = "select * from t_customer"; return qr.query(sql, new BeanListHandler<Customer>(Customer.class)); } catch(SQLException e) { throw new RuntimeException(e); } } /** * 加载客户 * @param cid * @return */ public Customer load(String cid) { try { String sql = "select * from t_customer where cid=?"; return qr.query(sql, new BeanHandler<Customer>(Customer.class), cid); } catch(SQLException e) { throw new RuntimeException(e); } } /** * 编辑客户 * @param c */ public void edit(Customer c) { try { String sql = "update t_customer set cname=?,gender=?,birthday=?," + "cellphone=?,email=?,description=? where cid=?"; Object[] params = {c.getCname(), c.getGender(), c.getBirthday(), c.getCellphone(), c.getEmail(), c.getDescription(), c.getCid()}; qr.update(sql, params); } catch(SQLException e) { throw new RuntimeException(e); } } /** * 多条件组合查询 * @param criteria * @return */ public List<Customer> query(Customer criteria) { try { /* * 1. 给出sql模板 * 2. 给出参数 * 3. 调用query方法,使用结果集处理器:BeanListHandler */ /* * 一、 给出sql模板 * 二、 给出参数! */ /* * 1. 给出一个sql语句前半部 */ StringBuilder sql = new StringBuilder("select * from t_customer where 1=1"); /* * 2. 判断条件,完成向sql中追加where子句 */ /* * 3. 创建一个ArrayList,用来装载参数值 */ List<Object> params = new ArrayList<Object>(); String cname = criteria.getCname(); if(cname != null && !cname.trim().isEmpty()) { sql.append(" and cname like ?"); params.add("%" + cname + "%"); } String gender = criteria.getGender(); if(gender != null && !gender.trim().isEmpty()) { sql.append(" and gender=?"); params.add(gender); } String cellphone = criteria.getCellphone(); if(cellphone != null && !cellphone.trim().isEmpty()) { sql.append(" and cellphone like ?"); params.add("%" + cellphone + "%"); } String email = criteria.getEmail(); if(email != null && !email.trim().isEmpty()) { sql.append(" and email like ?"); params.add("%" + email + "%"); } /* * 三、执行query */ return qr.query(sql.toString(), new BeanListHandler<Customer>(Customer.class), params.toArray()); } catch(SQLException e) { throw new RuntimeException(e); } } }