1. JDBC问题分析:
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try { // 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver"); // 通过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis? characterEncoding=utf-8", "root", "root"); // 定义sql语句?表示占位符
String sql = "select * from user where username = ?"; // 获取预处理statement
preparedStatement = connection.prepareStatement(sql); // 设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
preparedStatement.setString(1, "tom"); // 向数据库发出sql执行查询,查询出结果集
resultSet = preparedStatement.executeQuery(); // 遍历查询结果集
while (resultSet.next()) {
int id = resultSet.getInt("id");
String username = resultSet.getString("username"); // 封装User
user.setId(id);
user.setUsername(username);
}
System.out.println(user);
}} catch (Exception e)
{ e.printStackTrace(); }
finally { // 释放资源
if (resultSet != null) { try {resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (preparedStatement != null) { try {preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); }
1.1 加载驱动获取连接部分代码:
(1)数据库配置信息存在硬编码 -->配置文件
(2)频繁创建释放数据库连接-->连接池
1.2 sql语句部分代码:
(1).设置参数获取结果集均存在硬编码问题-->配置文件
1.3 封装返回结果代码:
手动封装,较为繁琐-->反射,内省
2. 自定义持久层框架设计思路(对以上JDBC问题进行规避)
2.1 使用端:(项目):
1.引入2.2的jar包
2.提供两部分的配置信息:
(1) 数据库配置信息以及mapper.xml的全路径:sqlMapConfig.xml
(2) sql配置信息(sql语句、参数类型、返回值类型):mapper.xml
2.2 自定义持久层框架本身(工程)本质就是对JDBC代码进行了封装
(客户端提供sqlMapConfig.xml+mapper.xml)-->Configuration对象(去那个数据库执行,执行什么)-->SqlSessionFactory-->SqlSession-->executer.query()
(1) 加载配置文件:根据配置文件的路径,加载配置文件成字节输入流,存储在内存中
创建Resouce类 方法: InputStream getResourceAsStream(String path)
(2)创建两个类:存放配置文件解析出来的内容
Configuration:存放sqlMapConfig.xml解析出来的内容
MappedStatement:存放mapper.xml解析出来的内容
(3)解析配置文件
class SqlSessionFactoryBuilder {
SqlSessionFactory builder(InputStream in) {
/**
* 使用dom4j解析配置文件,将解析出来的内容封装到容器对象中
* in-->Configuration
**/
/**
* 创建SqlSessionFactory对象,用来生产会话对象sqlSession
**/
SqlSessionFactory ssf = new DefaultSqlSessionFactory();//(4)
return ssf;
}
}
public class Configuration {
//对应于sqlMapConfig.xml
private DataSource dataSource;
/**
* 对应于多个mapper.xml文件
* key: statementId=namespance+"."+id
* value:封装好的一条语句信息
*/
private Map<String,MappedStatement> mappedStatementMap = new HashMap<>();
}
(4)创建sqlSessionFactory接口及实现类DefaultSqlSessionFactory;
interface SqlSessionFactory{
SqlSession openSession();
}
class DefaultSqlSessionFactory implements SqlSessionFactory{
@Override
SqlSession openSession() {//(5)
return new DefaultSqlSession();
}
}
(5)创建sqlSession接口及实现类DefaultSqlSession:定义对数据库的crud操作
interface SqlSession{
selectList();
selectOne();
update();
delete();
}
class DefaultSqlSession implements SqlSession{
Configuration configuration;
@Override
selectList() {
return new
SimpleExecutor().query(configuration,configuration.mapped***,params);//(6)
}
}
(6)创建Executor接口及实现类SimpleExecutor实现类
interface Executor{
query(Configuration,MappedStatement,Object... params);
}
class SimpleExecutor implements Executor{
@Override
query(Configuration,MappedStatement,Object... params) {
/**
* 执行的就是JDBC代码,
* 数据库配置和sql语句分别从Configuration和MappedStatement获取
* 参数值从params获取
**/
}
}
3. 自定义持久层框架问题分析
DaoImpl中:
(1)代码重复(加载配置文件,创建sqlSessionFactory,生产sqlSession)
(2) statementId存在硬编码问题
解决思路:去掉DaoImpl,使用代理模式生产Dao层接口的代理实现类
UserDao userDao = sqlSession.getMapper(UserDao.class);//userDao得到的是proxy对象
/**
* 代理对象调用接口中任意方法,都会执行invoke方法
**/
List<User> all = userDao.findAll();
/**
* getMapper的具体实现
*
**/
@Override
public <T> T getMapper(Class<?> mapperClass) {
//使用JDK动态代理来为Dao接口生成代理对象,并返回
final Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//proxy:当前代理对象的引用
//method:当前被调用方法的引用
//args:传递的参数
//底层都还是去执行JDBC代码//来根据不同情况来选择使用selectList和selectOne
return null;
}
});
return (T) proxyInstance;
}
|