3.3 基于XML的自定义mybatis框架

3.3.1 编写持久层接口和IUserDao.xml

1
2
3
4
5
6
7
8
9
10
11
12
/**
* <p>Title: IUserDao</p>
* <p>Description: 用户的持久层操作</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public interface IUserDao {
/**
* 查询所有用户
* @return
*/
List<User> findAll();
}
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?> 
<mapper namespace="com.itheima.dao.IUserDao">
<!-- 配置查询所有操作 -->
<select id="findAll" resultType="com.itheima.domain.User">
select * from user
</select>
</mapper>

注意: 此处我们使用的也是mybatis的配置文件,所以也要把约束删除了

3.3.2 编写构建者类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* <p>Title: SqlSessionFactoryBuilder</p>
* <p>Description: 用于构建SqlSessionFactory的</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class SqlSessionFactoryBuilder {
/**
* 根据传入的流,实现对SqlSessionFactory的创建
* @param in 它就是SqlMapConfig.xml的配置以及里面包含的IUserDao.xml的配置
* @return
*/
public SqlSessionFactory build(InputStream in) {
DefaultSqlSessionFactory factory = new DefaultSqlSessionFactory();
//给factory中config赋值
factory.setConfig(in);
return factory;
}
}

3.3.3 编写SqlSessionFactory接口和实现类

1
2
3
4
5
6
7
8
9
10
11
12
/**
* <p>Title: SqlSessionFactory</p>
* <p>Description: SqlSessionFactory的接口</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public interface SqlSessionFactory {
/**
* 创建一个新的SqlSession对象
* @return
*/
SqlSession openSession();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/** 
* <p>Title: DefaultSqlSessionFactory</p>
* <p>Description:SqlSessionFactory的默认实现 </p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private InputStream config = null;
public void setConfig(InputStream config) { this.config = config; }
@Override
public SqlSession openSession() {
DefaultSqlSession session = new DefaultSqlSession();
//调用工具类解析xml文件
XMLConfigBuilder.loadConfiguration(session, config);
return session;
}
}

3.3.4 编写SqlSession接口和实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* <p>Title: SqlSession</p>
* <p>Description: 操作数据库的核心对象</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public interface SqlSession {
/**
* 创建Dao接口的代理对象
* @param daoClass
* @return
*/
<T> T getMapper(Class<T> daoClass);
/**
* 释放资源
*/
void close();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/** 
* <p>Title: DefaultSqlSession</p>
* <p>Description: SqlSession的具体实现</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class DefaultSqlSession implements SqlSession {
//核心配置对象
private Configuration cfg;
public void setCfg(Configuration cfg) { this.cfg = cfg; }
//连接对象
private Connection conn;
//调用DataSourceUtils工具类获取连接
public Connection getConn() {
try {
conn = DataSourceUtil.getDataSource(cfg).getConnection();
return conn;
} catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* 动态代理:
* 涉及的类:Proxy
* 使用的方法:newProxyInstance
* 方法的参数:
* ClassLoader:和被代理对象使用相同的类加载器,通常都是固定的
* Class[]:代理对象和被代理对象要求有相同的行为。(具有相同的方法)
* InvocationHandler:如何代理。需要我们自己提供的增强部分的代码
*/
@Override
public <T> T getMapper(Class<T> daoClass) {
conn = getConn();
System.out.println(conn);
T daoProxy = (T) Proxy.newProxyInstance(daoClass.getClassLoader(),new Class[] {daoClass}, new MapperProxyFactory(cfg.getMappers(),conn));
return daoProxy;
}
//释放资源
@Override
public void close() {
try {
System.out.println(conn);
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//查询所有方法
public <E> List<E> selectList(String statement){
Mapper mapper = cfg.getMappers().get(statement);
return new Executor().selectList(mapper,conn);
}
}

3.3.5 编写用于创建Dao接口代理对象的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
* <p>Title: MapperProxyFactory</p>
* <p>Description: 用于创建代理对象是增强方法</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class MapperProxyFactory implements InvocationHandler {
private Map<String,Mapper> mappers;
private Connection conn;
public MapperProxyFactory(Map<String, Mapper> mappers,Connection conn) {
this.mappers = mappers;
this.conn = conn;
}
/**
* 对当前正在执行的方法进行增强
* 取出当前执行的方法名称
* 取出当前执行的方法所在类
* 拼接成key
* 去Map中获取Value(Mapper)
* 使用工具类Executor的selectList方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//1.取出方法名
String methodName = method.getName();
//2.取出方法所在类名
String className = method.getDeclaringClass().getName();
//3.拼接成Key
String key = className+"."+methodName;
//4.使用key取出mapper
Mapper mapper = mappers.get(key);
if(mapper == null) {
throw new IllegalArgumentException("传入的参数有误,无法获取执行的必要条件");
}
//5.创建Executor对象
Executor executor = new Executor();return executor.selectList(mapper, conn);
}
}

3.3.6 运行测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* <p>Title: MybatisTest</p>
* <p>Description: 测试mybatis的环境</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class MybatisTest {
public static void main(String[] args)throws Exception {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory的构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.使用构建者创建工厂对象SqlSessionFactory
SqlSessionFactory factory = builder.build(in);
//4.使用SqlSessionFactory生产SqlSession对象
SqlSession session = factory.openSession();
//5.使用SqlSession创建dao接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
//6.使用代理对象执行查询所有方法
List<User> users = userDao.findAll();
for(User user : users) {
System.out.println(user);
}
//7.释放资源
session.close();
in.close();
}
}