1.事务
引入案例:银行转账 A账户给B账户转账 如果转账中出现异常,事务就必须停止,且回滚。也就是转账过程,要么全部成功,要么全部失败,所以必须放在同一个事务
然而,Connection默认是事务自动提交的,要在同一个事务中操作,就必须多个关于数据的操作必须是同一个连接
保证操作中是同一个Connection就需要设置ThreadLocal操作
结构:
在此,我把数据的连接包装在一个类中,JdbcUtil,db.properties 如下:
private static Properties prop = new Properties();
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();//code1
static{
try {
prop.load(JdbcUtil.class.getResourceAsStream("/db.properties"));
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public static Connection getConnection(){
Connection connection = tl.get();//code2
try {
if (connection==null) {
Class.forName(prop.getProperty("jdbc.driverClass"));
connection = DriverManager.getConnection(prop.getProperty("jdbc.url"),prop.getProperty("jdbc.user"),prop.getProperty("jdbc.pass"));
tl.set(connection);//code3
}
return connection;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return null;
}
}
public static void close(Statement statement,ResultSet rs){
if (rs!=null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (statement!=null) {
try {
statement.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 业务按成后 连接才能关闭
* <p>Title: closeConnection</p>
* <p>Description: </p>
*/
public static void closeConnection(){
Connection connection = tl.get();
if(connection!=null){
try {
connection.close();
tl.remove();//code4
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
jdbc.driverClass=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@localhost:1521:ORCL
jdbc.user=scott
jdbc.pass=0804
Account.java
public class Account {
private int id;
private int money;
private String name;
public Account() {
// TODO Auto-generated constructor stub
}
public Account(int id, int money, String name) {
super();
this.id = id;
this.money = money;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Account [id=" + id + ", money=" + money + ", name=" + name + "]";
}
}
AccountService.java 接口类
public interface AccountService {
public void insertAccount(Account account);
public void updateAccount(Account account);
public List<Account>getAll();
}
AccountServiceImpl.java实现类
public class AccountServiceImpl implements AccountService{
@Override
public void insertAccount(Account account){
System.out.println("插入数据");
}
@Override
public void updateAccount(Account account){
System.out.println("更新数据");
}
@Override
public List<Account>getAll(){
System.out.println("获取数据");
return null;
}
}
因为是在同一线程中,Connection就不能随时close,否则,后面的操作将无法进行,所以,要单独写一个关闭Connection的方法,需要时再关闭
2.代理
为了避免在每个方法中都要写事务处理的问题,我们引入了代理,这样做就可以把dao和事务处理分离,开发人员做到各司其职,也就是AOP 面向方面的编程
代理分为静态代理和动态代理
2.1 静态代理
a. 创建一个代理类,实现与业务实现同一个接口,并且把代理类的实例作为自己的成员
AccountProxy.java 静态代理类
public class AccountProxy implements AccountService{
AccountServiceImpl asi;
public void setAsi(AccountServiceImpl asi) {
this.asi = asi;
}
@Override
public void insertAccount(Account account) {
// TODO Auto-generated method stub
System.out.println("开启事务");
asi.insertAccount(account);
System.out.println("提交事务,有问题就回滚事务");
}
@Override
public void updateAccount(Account account) {
// TODO Auto-generated method stub
System.out.println("开启事务");
asi.updateAccount(account);
System.out.println("提交事务,有问题就回滚事务");
}
@Override
public List<Account> getAll() {
// TODO Auto-generated method stub
System.out.println("开启事务");
asi.getAll();
System.out.println("提交事务,有问题就回滚事务");
return null;
}
}
测试代码
public class Demo1 {
public static void main(String[] args) {
AccountProxy ap = new AccountProxy();
AccountServiceImpl asi = new AccountServiceImpl();
ap.setAsi(asi);
ap.insertAccount(null);
ap.updateAccount(null);
}
}
输出结果:
静态代理缺点:
要做的辅助操作(事务,日志等)在每个业务方法中都需要进行调用,为了解决这个问题,我们引入动态代理
a. 动态代理处理器 必须实现 java.lang.reflect.InvocationHandle接口
b. 把目标对象(要生成代理的对象)作为成员
c. 实现invoke方法 该方法会拦截所有 的目标对象的方法 d
d. 产生代理对象,可以直接利用代理处理器,也可以单独写一个类
ProxyFactoryBean.java 动态代理类
public class ProxyFactoryBean implements InvocationHandler {
private Object target;//代理的目标对象-->调用目标对象的任何方法都会被invoke方法拦截
/*public void setTarget(Object target) {
this.target = target;
}*/
public ProxyFactoryBean(Object object) {
// TODO Auto-generated constructor stub
this.target = object;
}
/**
* 参数method 方法拦截后,拦截的目标对象的方法
* args就是方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("开启事务");
method.invoke(target, args);
System.out.println("提交事务,有问题就回滚事务");
return null;
}
public Object getProxy(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
测试代码
public class ProxyDemo2 {
public static void main(String[] args) {
AccountServiceImpl aServiceImpl = new AccountServiceImpl();//目标对象
//代理处理器,代理工厂
ProxyFactoryBean pfb = new ProxyFactoryBean(aServiceImpl);
//pfb.setTarget(aServiceImpl);
AccountService as = (AccountService)pfb.getProxy();
as.insertAccount(null);
}
}
测试结果
动态代理的缺点:
目标对象的所有方法都会被拦截处理
处理办法:
不过我们可以根据方法对象来控制,此处就不具体说明