test-> 中介[代理, 调用业务代码, 控制事务逻辑] -> 业务逻辑 -> DAO -> 数据库 实现
java动态代理实现访问数据库 dome
代码示例:
数据访问层代码:
dao 层代码
public interface UserDao {
/**
* 插入数据到数据库
* @param user
*/
void insertUser(User user);
/**
* 修改数据
* @param user
*/
void updateUser(User user);
}
daoImpl 层代码
public class UserDaoImpl implements UserDao {
/**
* 数据访问层代码,没有事务控制.
* 出现异常.需要抛出,通知业务代码.
* 业务代码根据异常,决定提交事务/回滚事务.
*/
@Override
public void insertUser(User user) {
SqlSession session = null;
try{
session = MyBatisUtils.openSession();
session.insert("insertUser", user);
}catch(Exception e){
e.printStackTrace();
throw e;
}
}
@Override
public void updateUser(User user) {
SqlSession session = null;
try{
session = MyBatisUtils.openSession();
session.update("updateUser", user);
}catch(Exception e){
e.printStackTrace();
throw e;
}
}
}
封装 Mybatis 工具类替代 JDBC 连接数据库
/**
* 框架工具代码. 实现创建会话对象的功能.
* 代码中封装一个会话工厂对象. 提供一个静态访问方法.获取会话对象.
*
* 分析:
* 会话工厂创建的个数,次数.
* 工厂创建一个. 创建一次.
*
* java中,什么代码可以保证,一次虚拟机生命周期,运行唯一一次.
*
* @author Administrator
*
*/
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory;
private static Map currentSessions = new HashMap<>();
/*
* 用户维护线程资源的一个控制对象.
* 可以保证,相同的线程中,只有唯一的一个资源对象,与之对应.
* 功能类似自定义的属性currentSessions
* 结构和currentSessions几乎一样.
* ThreadLocal中有属性,Map集合类型的属性.
* 集合的key是当前线程对象, 集合的value是要绑定的资源对象.
* 常用方法 :
* set(T t) -> map.put(Thread.currentThread(), t);
* get() -> map.get(Thread.currentThread());
* remove() -> map.remove(Thread.currentThread());
*/
private static ThreadLocal localSessions = new ThreadLocal<>();
static{
// 创建会话工厂的代码.
try{
sqlSessionFactory = new SqlSessionFactoryBuilder().build(
Resources.getResourceAsStream("mybatis.cfg.xml")
);
}catch(Exception e){
// 静态代码块出现异常.后续代码还能否执行? 中断JVM
// 抛出一个初始化代码异常错误.
throw new ExceptionInInitializerError(e);
}
}
/*
* 会话对象创建方法
* 功能增强. 一个线程,多次访问当前方法,只创建唯一的一个会话对象.
* 线程有对象. Thread类型的对象就是线程.
* 如何获取当前线程? Thread.currentThread();
* 使用当前线程对象作为key,SqlSession对象作为value,创建一个Map集合.管理会话对象.
*/
public static SqlSession openSession(){
// 1. 获取当前线程中对应的会话对象.
// SqlSession session = currentSessions.get(Thread.currentThread());
SqlSession session = localSessions.get();
// 2. 判断获取的会话对象是否为null
if(session == null){
// 当前线程未创建过会话对象.
session = sqlSessionFactory.openSession();
// currentSessions.put(Thread.currentThread(), session);
localSessions.set(session);
}
// 3. 返回结果
return session;
// return sqlSessionFactory.openSession();
}
/**
* 回收资源方法. 避免关闭会话对象后,在集合中仍然有会话对象的引用. 造成过时对象的应用.
*/
public static void close(){
SqlSession session = localSessions.get();
if(session != null){
session.close();
}
// 将关闭后的资源从集合中删除
localSessions.remove();
}
}
实体类
public class User {
// 属性叫field
private Integer id;
private String name;
private String password;
private int age;
private List address;
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", password=" + password
+ ", age=" + age + "]";
}
// get/set方法后的字符串成为property id - property
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return name;
}
public void setUsername(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
业务层代码 service
public interface UserService {
/**
* 注册用户
*/
void register(User user);
/**
* 修改用户
*/
void modify(User user);
}
业务层的实现类 service.impl
/**
* 业务层中,需要控制事务逻辑.
* 业务层需要访问数据库的时候, 调用DAO代码实现数据访问.
* @author Administrator
*
*/
public class UserServiceImpl implements UserService {
private UserDao userDao ;
/**
* 注册用户, 数据新增. 调用DAO实现数据的新增.
* 如果DAO代码正常执行结束,提交事务
* 如果DAO代码抛出异常,回滚事务.
*/
@Override
public void register(User user) {
System.out.println("UserServiceImpl.register运行");
this.userDao.insertUser(user);
}
@Override
public void modify(User user) {
this.userDao.updateUser(user);
}
}
动态代理类
/**
* 事务控制器. 用于处理事务控制逻辑的.
* 反射调用具体的业务代码.
*
* @author Administrator
*
*/
public class TransactionHandler implements InvocationHandler {
private Object target;
/**
* 方法是执行具体代码的方法.
* @param proxy - 代理对象. 就是代理业务代码的对象
* @param method - 运行的方法. 这个方法是真正的业务方法. 如: UserServiceImpl.login();
* @param args - 真正的业务方法,执行的时候,需要的参数.
* @return 真正的业务方法的返回值.
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 定义返回结果
Object returnValue = null;
SqlSession session = null;
// 执行具体的业务方法.
// 方法运行需要依赖真正的业务实现类对象.
try{
// 增加新的逻辑. 如: 事务控制
session = MyBatisUtils.openSession();
System.out.println("当前代理的方法是 : " + method.getName());
returnValue = method.invoke(target, args);
session.commit();
}catch(Exception e){
e.printStackTrace();
if(session != null)
session.rollback();
}finally{
MyBatisUtils.close();
}
return returnValue;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}
创建代理对象的工厂类
/**
* 用于创建代理对象的工厂.
* 功能是,动态的创建代理对象.
* 相当于使用反射技术,创建一个匿名内部类, 提供UserService接 口提供的所有方法.
*
* @author Administrator
*
*/
public class TransactionProxy {
// 目标对象. 要代理的真正的对象.
private Object target;
private InvocationHandler h;
/**
* 获取代理实例.
* 这个对象,是反射生成的对象.
* 对象通过Proxy创建.
* @return
*/
public Object getProxyInstance(){
ClassLoader loader = this.getClass().getClassLoader();
Class[] interfaces = this.target.getClass().getInterfaces();
/*
* @param loader - 类加载器. 用来反射生成对象的工具.
* @param interfaces - 生成的代理对象,需要实现的接口.
* @param h - 代理对象,在提供服务的时候,具体逻辑是什么. 之前定义的InvocationHandler
*/
Object proxy = Proxy.newProxyInstance(loader, interfaces, h);
return proxy;
}
public InvocationHandler getH() {
return h;
}
public void setH(InvocationHandler h) {
this.h = h;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}
Until测试
@Test
public void testDynamicProxy(){
//
UserService userService = new UserServiceImpl();
((UserServiceImpl)userService).setUserDao(new UserDaoImpl());
//创建代理工厂
TransactionProxy proxyFactory = new TransactionProxy();
//创建代理
TransactionHandler h = new TransactionHandler();
h.setTarget(userService);
proxyFactory.setH(h);
proxyFactory.setTarget(userService);
UserService service = (UserService) proxy;
//创建用户对象
User user = new User();
user.setAge(25);
user.setUsername("张三");
user.setPassword("250");
//实现注册
service.register(user);
}