1. 回顾 jdbcTemplate
知识准备:可参考之前总结的内容 - 从 jdbc 到 jdbcTemplate
1.1 jdbcTemplate 的设计初衷
- jdbcTemplate 对 jdbc 的简单封装,是简化操作数据库的一种手段;
传统 jdbc 的实现
- jdbc 是一套操作所有关系型数据库的规范(接口),由数据库厂商实现接口并提供驱动 jar 包;
- 基本操作步骤:
- 注册驱动: Class.forName(“com.mysql.jdbc.Driver“);【前提是提供驱动 jar 包】
- 获取数据库连接对象:conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/base_name”,”user”,”password”);
- 获取执行 sql 的对象:pstmt=conn.prepareStatement(“sql 语句”);
- 执行 sql:pstmt.executeUpdate() or pstmt.executeQuery();
- 结果处理:主要用于将 executeQuery 查询的结果集封装成 domain 对象;
- 释放资源:pstmt.close(); conn.close();
- 存在的问题
- “不变”所带来的“代码重复”问题
- 驱动、连接对象、执行 sql 的对象、执行方法(update、query)、释放资源是“不变”的;
- 查询结果集 resultSet 到 domain 对象的映射过程(创建 domian 对象,匹配映射字段,set)是“不变”的;
- 可“变”参数注入所带来的“耦合”问题
- sql 语句、查询结果集 resultSet 映射的 domain 对象是“变”的;
- 查询结果集 resultSet 映射时需指定 domain 的对象;
- 执行方法需传入的 sql 语句;
- 连接对象的频繁构建与销毁所带来的资源损耗和浪费问题;
1.2 jdbcTemplate 的设计方案
1.2.1 jdbcTemplate 改进了什么
- “不变”所带来的“代码重复”问题
jdbcUtil:将“不变”的【注册驱动、创建连接对象、释放资源】抽象成工具包 ;
- dataSource:针对创建与销毁连接对象所带来的资源问题用“池”的手段解决;
- BeanRowMapper:利用 反射技术 实现查询结果集 resultSet 与 指定 domain 对象的映射过程;
- newInstance(反射创建 domain 对象)、getFields(获取类对象的所有字段) 等;
JdbcTemplate:内置 BeanRowMapper 的结果映射方法,对普通执行对象 prepareStatement 的替代;
1.2.2 jdbcTemplate 待改进的地方
jdbcTemplate 主要改进的是“代码重复”问题,利用的手段主要是“独立工具包”、“池”、“反射”;
- 待改进之处主要在于“如何解耦”:执行过程中注入的 sql ,返回结果指定的 domain 类对象;
2. mybatis 的框架设计
2.1 mybatis 在 jdbcTemplate 基础上的改进
由于 mybatis 是对 jdbcTemplate 的进一步封装,其应用目的依然是对数据库进行操作,所以针对 mybatis 框架,我们需要从两个方面进行了解:
- dao 层实现:IUerDao 接口,对应的 domain 对象为 User,User 包括:id、username、address、sex、birthday 这 5 个字段; ```java package com.cyt.dao;
import com.cyt.domain.User; import java.util.List;
public interface IUserDao {
// 查询所有用户
List
// 查询单个用户
User findUserById(Integer id);
// 根据名称查找用户
List<User> findByName(String name);
// 查询记录总数
Integer findTotal();
// 保存用户
void saveUser(User user);
// 更新用户
void updateUser(User user);
// 根据 id 删除用户
void deleteUser(Integer id);
}
- 2. 配置 IUserDao.xml,即 IUserDao 接口对应的映射文件
```java
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cyt.dao.IUserDao">
<select id="findAll" resultType="User">
select * from user
</select>
<select id="findUserById" parameterType="java.lang.Integer" resultType="com.cyt.domain.User" useCache="true">
select * from user where id=#{id}
</select>
<select id="findByName" parameterType="string" resultType="com.cyt.domain.User">
select * from user where username like '%${value}%'
</select>
<select id="findTotal" resultType="java.lang.Integer">
select count(*) from user;
</select>
<insert id="saveUser" parameterType="com.cyt.domain.User">
<!-- 配置插入操作后,获取插入数据的 id ,其中keyProperty对应的是实体类中的名称-->
<selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
select last_insert_id();
</selectKey>
<!-- 字段的顺序必须和 User 类中的类成员变量顺序一致,且 #()取值与 getXXX 一致-->
insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday});
</insert>
<update id="updateUser" parameterType="com.cyt.domain.User">
update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id};
</update>
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id=#{user_id};//此处对名称无要求
</delete>
</mapper>
- 配置 SqlMapConfig.xml
- mybatis 的主配置文件,利用其中信息可连接数据库,并找到存储 sql 语句(dao 层的映射文件)的地方;
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 配置环境 -->
<environments default="mysql">
<!-- 配置 mysql 的环境-->
<environment id="mysql">
<!-- 配置事务的类型 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池)-->
<dataSource type="POOLED">
<!-- 配置连接数据库的 4 个基本信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/cyt_mybatis"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
<!-- 此处记录一个报错:property name 的顺序和内容必须如上一致,否则会报错 BuilderException:Error parsing SQL Mapper Configuration.. unknown DataSource property user-->
</environment>
</environments>
<!-- 指定映射配置文件的位置,映射配置文件指的是每个 dao 独立的配置文件,如果使用注解来配置的话,此处应使用 class 属性指定被注解的 dao 全限定类名-->
<mappers>
<mapper resource="com/cyt/dao/IUserDao.xml"/>
</mappers>
</configuration>
- mybatis 的主配置文件,利用其中信息可连接数据库,并找到存储 sql 语句(dao 层的映射文件)的地方;
- 配置 SqlMapConfig.xml
- 测试:调用 IUserDao 接口的方法,实现简单的 CURD; ```java package com.cyt.test;
import com.cyt.domain.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test;
import java.io.InputStream; import java.util.Date; import java.util.List;
public class MybatisTest {
private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;
@Before
public void init() throws Exception{
in = Resources.getResourceAsStream("SqlMapConfig.xml");//读取 mybatis 主配置文件
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
sqlSession = factory.openSession(true);
userDao = sqlSession.getMapper(IUserDao.class);
}
@After
public void desTroy() throws Exception{
sqlSession.close();
in.close();
}
@Test
public void testFindAll() throws Exception{
List<User1> users = userDao.findAll();
for (User1 u : users) {
System.out.println(u);
}
}
@Test
public void tesFindUsersByName() throws Exception{
List<User> users = userDao.findByName("陈玉婷");
for (User u : users) {
System.out.println(u);
}
}
@Test
public void testSaveUser() throws Exception{
User user = new User();
user.setUsername("陈玉婷_annotationInsert");
user.setAddress("天津财经大学~~~.....................");
user.setSex("女");
user.setBirthday(new Date());
System.out.println(user);
userDao.saveUser(user);
System.out.println(user);
}
@Test
public void testUpdateUser() throws Exception{
int id = 64;
User user = userDao.findUserById(id);
user.setAddress(user.getAddress() + "update");
userDao.updateUser(user);
}
@Test
public void testDeleteUser() throws Exception{
userDao.deleteUser(65);
}
}
<a name="HwlLe"></a>
#### 2.1.1.2 对比 jdbcTemplate,mybatis 是如何操作数据库的
- 先来总结下 jdbcTemplate 操作数据库的步骤
- 1. **获取连接池对象**:DataSource = JdbcPoolUtil.getDS(),其中 JdbcPollUtil 是 C3P0 数据库连接池工具类,封装了连接数据库和释放连接的基本方法;
- 2. **创建 JdbcTemplate 对象**:template = new JdbcTemplate(ds);
- 3. **执行 sql 语句,并获取结果**
- int count = template.update(sql);//影响的行数
- List<IUserDao> list template.query(sql, new BeanPropertyRowMapper<IUserDao>(IUserDao.class)); // 查询结果集映射的集合对象
- 4. 释放资源,连接池会自动回收连接;
- 从 **2.2.1.1 的案例** 来总结 mybatis 操作数据库的基本步骤
- 1. **读取** mybatis 主配置文件 **SqlMapConfig.xml** 来获取两类信息,一类是连接数据库所需的 driver、url、name 和 password,另一类是 dao 层对应的存储 sql 语句的映射文件位置;
- in = Resources.getResourceAsStream("SqlMapConfig.xml");
- 2. 利用步骤1所获得的信息,通过 SqlSessionFactoryBuilder 、SqlSessionFactory **获取 sqlSession**;
- SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);<br /> ** **sqlSession = factory.openSession(true);
- 3. sqlSession 本质是存储 IUserDao.xml 映射 sql 语句的“大 mapper”,方便**获取 dao 层对象**;
- userDao = sqlSession.getMapper(IUserDao.class);
- 4. **调用 dao 层方法**,如 List<User> users = userDao.findAll();
- 5.** 释放资源**:sqlSession.close(); in.close();
- 对比 jdbcTemplate,mybatis 操作数据库的步骤
- **共同点-建立与释放连接**:
- 从某个配置类或配置文件中获取连接对象,并且执行操作之后都要释放资源;
- **不同点-获取结果的过程**:
- jdbcTemplate 显式调用 sql 语句,需手动注入 sql 语句和待映射的 dao 层类对象来获取结果;
- mybatis 将执行逻辑赋予更贴近上层的 dao 对象,隐藏了 jdbcTemplate 执行 sql 语句的过程;
<a name="b9fSh"></a>
### 2.1.2 mybatis 如何改进 jdbc 的“过耦合”问题
<a name="qczs1"></a>
#### 2.1.2.1 借鉴 spring 的“解耦”思路
- spring 如何“解耦”
- “静态”上的设计
- 通过不同的配置文件创建不同层次的“容器”,并在容器中维护bean 对象的生命周期;
- 将 bean 对象保存在一张大“mapper”表中,方便查找获取;
- “动态”上的实现
- 解析配置文件生成上下文环境;
- 通过一定的映射机制从 mapper 中动态获取 bean 对象,并实现自动装配和组合;
- 小结:spring 的解耦技巧
- 利用“容器”存储那些想要实现动态注入的对象;
- 为了方便找到“容器”中的对象,需要提供一定的映射规则,比如:id、全限定名等;
- 解析配置文件时主要是将这些“容器”中的对象保存到 mapper 中,这样在调用时能方便地获取到对象;
<a name="A0Ps9"></a>
#### 2.1.2.2 探寻 mybatis 的本质及“解耦”实现
![mvc1.png](https://cdn.nlark.com/yuque/0/2020/png/611598/1584002674158-0cff1480-f1f6-4ef9-a4f5-9df9e26d8d20.png#align=left&display=inline&height=340&name=mvc1.png&originHeight=340&originWidth=1361&size=50064&status=done&style=none&width=1361)
- 对 mybatis 的大体认知
- 从上图可以看出,mybatis 的本质是隐藏 jdbcTemplate 具体执行 sql 的过程,通过 sqlSession 这样类代理对象存储类路径下能够真正执行不同 sql 语句的唯一 template 对象,而且通过 mapper 表和一定的映射机制,实现 sql 语句和映射 domain 对象的动态注入,这样做的好处是将有关 jdbc 的底层处理逻辑向上层抽象,最终以业务逻辑的形式展现;
- mybatis 的“解耦”实现
- 两个容器
- 一个是基础环境配置文件,包含创建数据库连接的基本信息和存储 sql 语句文件的路径信息;
- 另一个是存储 sql 语句的文件,包含对应接口所有方法用到的 sql 语句,输入和返回参数信息等;
- 创建代理对象 SqlSession
- 通过动态注入 XXXdao 类对象,动态创建 XXXdao.xml 配置 sql 文件的 mapper 表,mapper 的 key 对对应 dao 接口方法的命名 id,value 为 sql 语句及相关的参数信息等;
<a name="dK9U4"></a>
## 2.2 自定义 mybatis
<a name="qbAlx"></a>
### 2.2.1 代码
- 目录
![image.png](https://cdn.nlark.com/yuque/0/2020/png/611598/1584003460004-e81bd734-a8ec-4f9d-b6fe-d06ed102d69b.png#align=left&display=inline&height=181&name=image.png&originHeight=361&originWidth=255&size=18755&status=done&style=none&width=127.5)
- 注:以下按着 mybatis 操作数据库的步骤创建所需要的类
- 1. 使用类加载器读取配置文件
```java
public class Resources {
public static InputStream getResourceAsStream(String filePath){
return Resources.class.getClassLoader().getResourceAsStream(filePath);//根据传入的参数,获取一个字节输入流
}
}
- 创建能够生成 sqlSession 的相关类
- 1 三个 Proxy:一个调用入口类,两个接口
// 用于创建一个 SqlSessionFactory 对象
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream config){
Configuration cfg = XMLConfigBuilder.loadConfiguration(config);//根据参数的字节输入流来创建一个 SqlSessionFactory 工厂
return new DefaultSqlSessionFactory(cfg);
}
}
// 用于打开一个新的 session 对象
public interface SqlSessionFactory {
SqlSession openSession();
}
// 自定义 Mybatis 中和数据库交互的核心类,它里面可以创建 dao 接口的代理对象
public interface SqlSession {
<T> T getMapper(Class<T> daoInterfaceClass);//根据参数创建一个代理对象
void close();
}
- 1 三个 Proxy:一个调用入口类,两个接口
- 创建能够生成 sqlSession 的相关类
2.2 针对两个 Proxy 接口的实现类
// SqlSessionFactory 接口的实现类 public class DefaultSqlSessionFactory implements SqlSessionFactory { private Configuration cfg; public DefaultSqlSessionFactory(Configuration cfg){ this.cfg = cfg; } @Override public SqlSession openSession() { return new DefaultSqlSession(cfg);//用于创建一个新的操作数据库对象 } } // SqlSession 接口的实现类 public class DefaultSqlSession implements SqlSession { private Configuration cfg; private Connection connection; public DefaultSqlSession(Configuration cfg){ this.cfg = cfg; connection = DataSourceUtil.getConnection(cfg); } // 用于创建代理对象,不指定返回类型的话,会报错 public <T> T getMapper(Class<T> daoInterfaceClass) { return (T)Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(), new Class[]{daoInterfaceClass}, new MapperProxy(cfg.getMappers(), connection)); } //用于释放连接资源 public void close() { if (connection != null) { try { connection.close(); } catch (Exception e) { e.printStackTrace(); } } } }
- 配置(SqlMapConfig.xml)和映射文件(IUserDao.xml)所对应的两个类对象
- SqlMapConfig.xml 对应 Configuration 类,用于存储创建数据库连接的信息和映射文件的存储路径信息;
- IUserDao.xml 对应 Mapper 类,存储该 Dao 接口中每个方法对应的 sql 语句和可能返回的结果类型;
```java
// 自定义 Mybatis 的配置类,省略 getter/setter 和 toString
public class Configuration {
private String driver;
private String url;
private String username;
private String password;
private Map
mappers = new HashMap (); } // 用于封装执行的 SQL 语句和结果类型的全限定类名,省略 getter/setter 和 toString public class Mapper { private String queryString;//SQL private String resultType;//实体类的全限定类名 }
- 配置(SqlMapConfig.xml)和映射文件(IUserDao.xml)所对应的两个类对象
- 4. 三个工具类
- 4.1 用于创建数据源的工具类
```java
public class DataSourceUtil {
// 用于获取一个数据库连接
public static Connection getConnection(Configuration cfg){
try {
Class.forName(cfg.getDriver());
return DriverManager.getConnection(cfg.getUrl(),cfg.getUsername(),cfg.getPassword());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
4.2 mybatis 的动态执行器 Executor,执行 SQL 语句并封装结果集,释放资源;
public class Executor { // selectList() 方法负责执行 SQL 语句,并且封装结果集 public <E> List<E> selectList(Mapper mapper, Connection conn) { PreparedStatement pstm = null; ResultSet rs = null; try { //1.取出mapper中的数据 String queryString = mapper.getQueryString();//select * from user String resultType = mapper.getResultType();//com.cyt.domain.User Class domainClass = Class.forName(resultType); //2.获取PreparedStatement对象 pstm = conn.prepareStatement(queryString); //3.执行SQL语句,获取结果集 rs = pstm.executeQuery(); //4.封装结果集 List<E> list = new ArrayList<E>();//定义返回值 while(rs.next()) { //实例化要封装的实体类对象 E obj = (E)domainClass.newInstance(); //取出结果集的元信息:ResultSetMetaData ResultSetMetaData rsmd = rs.getMetaData(); //取出总列数 int columnCount = rsmd.getColumnCount(); //遍历总列数 for (int i = 1; i <= columnCount; i++) { //获取每列的名称,列名的序号是从1开始的 String columnName = rsmd.getColumnName(i); //根据得到列名,获取每列的值 Object columnValue = rs.getObject(columnName); //给obj赋值:使用Java内省机制(借助PropertyDescriptor实现属性的封装) PropertyDescriptor pd = new PropertyDescriptor(columnName,domainClass);//要求:实体类的属性和数据库表的列名保持一种 //获取它的写入方法 Method writeMethod = pd.getWriteMethod(); //把获取的列的值,给对象赋值 writeMethod.invoke(obj,columnValue); } //把赋好值的对象加入到集合中 list.add(obj); } return list; } catch (Exception e) { throw new RuntimeException(e); } finally { release(pstm,rs); } } // release() 方法释放资源 private void release(PreparedStatement pstm,ResultSet rs){ if(rs != null){ try { rs.close(); }catch(Exception e){ e.printStackTrace(); } } if(pstm != null){ try { pstm.close(); }catch(Exception e){ e.printStackTrace(); } } } }
4.3 解析配置文件的类 XMLConfigBuilder,SqlSesson 主要通过此方法创建连接,并获取 mapper;
- 该方法针对 xml 和 annotation 有不同的创建 mapper 的方法; ```java public class XMLConfigBuilder {
/**
- 解析主配置文件,把里面的内容填充到 DefaultSqlSession 所需要的地方
- 使用的技术:
- dom4j+xpath
*/
public static Configuration loadConfiguration(InputStream config){
try{
//定义封装连接信息的配置对象(mybatis的配置对象)
Configuration cfg = new Configuration();
//1.获取SAXReader对象
SAXReader reader = new SAXReader();
//2.根据字节输入流获取Document对象
Document document = reader.read(config);
//3.获取根节点
Element root = document.getRootElement();
//4.使用xpath中选择指定节点的方式,获取所有property节点
List
propertyElements = root.selectNodes(“//property”); //5.遍历节点 for(Element propertyElement : propertyElements){
} //取出mappers中的所有mapper标签,判断他们使用了resource还是class属性 List//判断节点是连接数据库的哪部分信息 //取出name属性的值 String name = propertyElement.attributeValue("name"); if("driver".equals(name)){ //表示驱动 //获取property标签value属性的值 String driver = propertyElement.attributeValue("value"); cfg.setDriver(driver); } if("url".equals(name)){ //表示连接字符串 //获取property标签value属性的值 String url = propertyElement.attributeValue("value"); cfg.setUrl(url); } if("username".equals(name)){ //表示用户名 //获取property标签value属性的值 String username = propertyElement.attributeValue("value"); cfg.setUsername(username); } if("password".equals(name)){ //表示密码 //获取property标签value属性的值 String password = propertyElement.attributeValue("value"); cfg.setPassword(password); }
mapperElements = root.selectNodes(“//mappers/mapper”); //遍历集合 for(Element mapperElement : mapperElements){
// System.out.println(“mapperPath:” + mapperPath);//判断mapperElement使用的是哪个属性 Attribute attribute = mapperElement.attribute("resource"); if(attribute != null){ System.out.println("使用的是XML"); //表示有resource属性,用的是XML //取出属性的值 String mapperPath = attribute.getValue();//获取属性的值"com/cyt/dao/IUserDao.xml"
} //返回Configuration return cfg; }catch(Exception e){ throw new RuntimeException(e); }finally{ try {//把映射配置文件的内容获取出来,封装成一个map Map<String, Mapper> mappers = loadMapperConfiguration(mapperPath); //给configuration中的mappers赋值 cfg.setMappers(mappers); }else{ System.out.println("使用的是注解"); //表示没有resource属性,用的是注解 //获取class属性的值 String daoClassPath = mapperElement.attributeValue("class"); //根据daoClassPath获取封装的必要信息 Map<String,Mapper> mappers = loadMapperAnnotation(daoClassPath); //给configuration中的mappers赋值 cfg.setMappers(mappers); }
}catch(Exception e){config.close();
} } }e.printStackTrace();
/**
- 根据传入的参数,解析XML,并且封装到Map中
- @param mapperPath 映射配置文件的位置
- @return map中包含了获取的唯一标识(key是由dao的全限定类名和方法名组成)
- 以及执行所需的必要信息(value是一个Mapper对象,里面存放的是执行的SQL语句和要封装的实体类全限定类名)
*/
private static Map
loadMapperConfiguration(String mapperPath)throws IOException { InputStream in = null; try{ //定义返回值对象 Map mappers = new HashMap (); //1.根据路径获取字节输入流 in = Resources.getResourceAsStream(mapperPath); //2.根据字节输入流获取Document对象 SAXReader reader = new SAXReader(); Document document = reader.read(in); //3.获取根节点 Element root = document.getRootElement(); //4.获取根节点的namespace属性取值 String namespace = root.attributeValue(“namespace”);//是组成map中key的部分 // System.out.println(“namespace:”+namespace); //5.获取所有的select节点 List selectElements = root.selectNodes(“//select”); //6.遍历select节点集合 for(Element selectElement : selectElements){ //取出id属性的值 组成map中key的部分 String id = selectElement.attributeValue(“id”); //取出resultType属性的值 组成map中value的部分 String resultType = selectElement.attributeValue(“resultType”); //取出文本内容 组成map中value的部分 String queryString = selectElement.getText(); //创建Key String key = namespace+”.” + id; //创建Value Mapper mapper = new Mapper(); mapper.setQueryString(queryString); mapper.setResultType(resultType); //把key和value存入mappers中 mappers.put(key,mapper); } return mappers; }catch(Exception e){ throw new RuntimeException(e); }finally{ in.close(); } }
/**
- 根据传入的参数,得到dao中所有被select注解标注的方法。
- 根据方法名称和类名,以及方法上注解value属性的值,组成Mapper的必要信息
- @param daoClassPath
@return */ private static Map
loadMapperAnnotation(String daoClassPath)throws Exception{ //定义返回值对象 Map mappers = new HashMap (); //1.得到dao接口的字节码对象 Class daoClass = Class.forName(daoClassPath); //2.得到dao接口中的方法数组 Method[] methods = daoClass.getMethods(); //3.遍历Method数组 for(Method method : methods){
//取出每一个方法,判断是否有select注解 boolean isAnnotated = method.isAnnotationPresent(Select.class); if(isAnnotated){ //创建Mapper对象 Mapper mapper = new Mapper(); //取出注解的value属性值 Select selectAnno = method.getAnnotation(Select.class); String queryString = selectAnno.value(); mapper.setQueryString(queryString); //获取当前方法的返回值,还要求必须带有泛型信息 Type type = method.getGenericReturnType();//List<User> //判断type是不是参数化的类型 if(type instanceof ParameterizedType){ //强转 ParameterizedType ptype = (ParameterizedType)type; //得到参数化类型中的实际类型参数 Type[] types = ptype.getActualTypeArguments(); //取出第一个 Class domainClass = (Class)types[0]; //获取domainClass的类名 String resultType = domainClass.getName(); //给Mapper赋值 mapper.setResultType(resultType); } //组装key的信息 //获取方法的名称 String methodName = method.getName(); String className = method.getDeclaringClass().getName(); String key = className+"."+methodName; //给map赋值 mappers.put(key,mapper); }
} return mappers; }
2.2.2 过程图分析
2.3 mybatis 还能怎么优化
- 在 mybatis 现有框架下,提供两个配置文件,一个负责连接任务(一端连接数据库,另一端连接存储 sql 的文件),另一个负责存储对应接口方法相关 sql 信息;
- 注:以下都是基于目前认知所提出的小猜想,可能不成熟,也可能错误,待以后验证;
- 从代码优化角度考虑
- 可以定义一套规范来简写 SQL,又或者能够以某种更简单的方式拼接 SQL;
- 从内存角度考虑
- 可以精简 dao 层对应的 mapper 信息,尤其是其中的 sql 信息,可以用的时候动态组装起来,从这个角度考虑,是不是 SQL 也能像 class 对象以某种字节码实现呢?
从性能角度考虑
dao 层为什么是接口类:
- 因为 sqlSession.getMapper() 中使用的是动态代理;
- dao 类与 对应的 mapper.xml 是如何映射的
- 由 sqlMapConfig.xml 提供 sql 映射路径和 sql 语句对应的方法(或自定义 id)为 key,sql 语句相关信息为 value;
- mybatis 是如何处理依赖注入的
- select from user where id=*#{id}
- mapper.xml 中的 sql 语句有“生命对象”的特性吗?
- 待查资料;