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
条件查询
修改客户
按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);
}
}
}