ThreadLocal方法 :
set(Object);设置值,由于只存一个值,后面的会覆盖前面的。
get();获取对象的值
remove()移除设置的值
java事务
connection.setAutoCommit(false);开启事务
connection.commit();提交事务
connection.rollback();回滚事务,写在catch方法里。
页面内容
一。DruidUtil 工具类
public class DruidUtil {
private static DataSource dataSource;
private static ThreadLocal threadLocal=new ThreadLocal();//threadLocal线程只存一个值且与其他线程独立
static {
try {
//1. 创建Properties对象
Properties properties = new Properties();
//2. 将配置文件转换成字节输入流
InputStream is = DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties");
//3. 使用properties对象加载is
properties.load(is);
//druid底层是使用的工厂设计模式,去加载配置文件,创建DruidDataSource对象
dataSource = DruidDataSourceFactory.createDataSource(properties);
threadLocal.set(dataSource.getConnection());//在static代码块,类加载的时候就存个连接进去,类只会加载一次,threadLocal也只有一个链接
} catch (Exception e) {
e.printStackTrace();
}
}
public static DataSource getDataSource(){
return dataSource;
}
public static Connection getConnection() throws SQLException {
return (Connection) threadLocal.get();
//返回连接threadLocal.get()拿到值
}
}
二。AccountServlet
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
//1. 接收请求: 获取请求参数
String fromName = request.getParameter("from");//转出账户名
String toName = request.getParameter("to");//转入账户名
Double money = Double.valueOf(request.getParameter("money"));//转账金额
try {
//调用业务层的方法,进行转账
AccountService accountService = new AccountService();
accountService.transfer(fromName,toName,money);
//转账成功
response.getWriter().write("转账成功!!!");
//这里的catch用来捕获从业务层出错抛出的异常,业务层是逻辑所以会在业务层处理逻辑
} catch (Exception throwables) {
throwables.printStackTrace();
response.getWriter().write("转账失败");
}
}
public void transfer(String fromName, String toName, Double money) throws Exception {
Connection connection = null;
try {
//开启事务
connection = DruidUtil.getConnection();
//connection不自动提交即开启事务
connection.setAutoCommit(false);
AccountDao accountDao = new AccountDao();
//1. 进行付款方扣款
//accountDao.updateAccountMoney(connection,fromName, -money);v2传连接的版本
//方法里不用带连接,因为这里的druidutil用了threadlocal获得到的连接都是同一个连接,无论是service开启事务的connection还是dao用connection操作数据库都是同一个连接。
accountDao.updateAccountMoney(fromName, -money);
// int num = 10 / 0;
//2. 进行收款方收款
// accountDao.updateAccountMoney(connection,toName, money); v2
accountDao.updateAccountMoney(toName, money);
//提交事务
connection.commit();
}
//在catch里表示程序出错,事务进行回滚。
catch (Exception e) {
e.printStackTrace();
//回滚事务
connection.rollback();
//主动抛出异常让servlet捕获servlet处理异常
throw new RuntimeException(e.getMessage());
}
}
}
public class AccountDao {
//这里的 new QueryRunner();不用带初始值Druid.getDataSource(),因为下方的方法里的queryrunner第一个参数会带connection处理数据库。
private QueryRunner queryRunner = new QueryRunner();
private Connection connection;
{
try {
connection = DruidUtil.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
}
//从service传递连接的方法,保证在业务层开启事务的连接和持久层里的连接时同一个连接,但是这样耦合太高了,复用性低。
// public void updateAccountMoney(Connection connection, String name, Double money) throws Exception {
// String sql = "update account set money=money+? where username=?";
// //使用指定的连接执行SQL语句,而不是从连接池随便拿一个连接
// queryRunner.update(connection,sql,money,name);
// }
public void updateAccountMoney( String name, Double money) throws Exception {
String sql = "update account set money=money+? where username=?";
//使用指定的连接执行SQL语句,而不是从连接池随便拿一个连接
queryRunner.update(connection,sql,money,name);
}
}