自定义Mybatis
自定义mybatis:编写SqlSessionFactory和SqlSession
目标
编写SqlSessionFactory和SqlSession
复制入门案例模块,修改模块名为:mybatis01_02_framework,导入模块
删除配置文件中的DTD约束,不然DOM解析会联网,会失败
编写SqlSessionFactory类
- 新建
com.itheima.mybatis
包 - 在
com.itheima.mybatis
创建SqlSessionFactory类
SqlSessionFactory的作用是创建
SqlSession
public class SqlSessionFactory { public SqlSession openSession() { return new SqlSession(); } }
编写SqlSession类
在
com.itheima.mybatis创建SqlSession类
SqlSession的作用是获取
Mapper的代理对象
UserMapper userMapper = session.getMapper(UserMapper.class);
import java.lang.reflect.Proxy; // SqlSession的作用是获取Mapper的代理对象 public class SqlSession { /* Object newProxyInstance(ClassLoader loader, 类加载器 Class<?>[] interfaces, 接口 InvocationHandler h) 调用处理器 */ public <T> T getMapper(Class<T> type) { // 生成参数的代理对象 return (T)Proxy.newProxyInstance( this.getClass().getClassLoader(), new Class[] {type}, new MyInvocationHandler() ); } }
Mapper的代理对象是用来执行SQL语句的
List<User> users = userMapper.findAllUsers();
我们先固定写一些数据
public class MyInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 返回查询数据 ArrayList<User> list = new ArrayList<>(); list.add(new User(2, "张三", new Date(100), "男", "广州")); list.add(new User(3, "李四", new Date(200), "女", "深圳")); return list; } }
小结
-
说出SqlSessionFactory的作用
用于创建SqlSession
-
说出SqlSession的作用
getMapper() 产生接口的代理对象
自定义mybatis:设计Mapper类
目标
编写Mapper类封装UserMapper.xml数据
分析UserMapper.xml
步骤
- 创建Mapper实体类:包含4个属性:namespace,id,resultType,sql
- 生成get和set方法
代码
package com.itheima.mybatis; /** 用来封装映射文件的实体类 一个Mapper对象代表一条要操作的查询语句对象 */ public class Mapper { private String namespace; //接口类全名 private String id; //接口中方法名 private String resultType; //封装的数据类型 private String sql; //要执行的SQL语句 // 省略getter/setter }
小结
Mapper实体类中有哪几个属性?
- namespace
- id
- resultType
- sql
自定义mybatis:设计Configuration类
目标
编写Configuration类
分析
sqlMapConfig.xml文件
编写Configuration类步骤
- 创建driver,url, username,password四个属性
- 实例化1个空的Map集合:封装接口映射文件的XML信息
- 声明数据源对象DataSource
- 生成get和set方法,生成toString()方法
代码
package com.itheima.mybatis; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; /** 1.封装sqlMapConfig.xml配置信息 2.得到数据源 3.加载UserMapper.xml配置信息 */ public class Configuration { // 数据源的四个属性 private String username; private String password; private String url; private String driver; // 封装其它的映射文件中属性 private Map<String, Mapper> mappers = new HashMap<>(); private DataSource dataSource; //数据源 // 省略getter/setter }
小结
Configuration有哪些属性?
private String driver; 驱动类名 private String url; 数据url private String username; 数据库账号 private String password; 数据库密码 private HashMap<String, Mapper> mappers = new HashMap<>(); // 对应接口映射文件数据 private DruidDataSource dds; // 连接池
自定义mybatis:在Configuration中解析sqlMapConfig.xml核心配置文件
目标
解析核心配置文件sqlMapConfig.xml,给Configuration中的成员变量赋值,创建连接池
分析
实现步骤
编写loadSqlMapConfig方法:使用dom4j解析sqlMapConfig.xml文件,给数据库有关的属性赋值
- 从类路径加载sqlMapConfig.xml配置文件,创建输入流
- 使用dom4j得到文档对象
- 使用XPath读取每个property元素,读取它的name和value属性值
- 给对象的属性赋值
- 创建Druid连接池
Configuration代码
package com.itheima.mybatis; import com.alibaba.druid.pool.DruidDataSource; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.Node; import org.dom4j.io.SAXReader; import javax.sql.DataSource; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; /* 1.封装sqlMapConfig.xml配置信息 2.得到数据源 3.加载UserMapper.xml配置信息 */ public class Configuration { // 数据源的四个属性 private String driver; private String url; private String username; private String password; // 数据源 private DataSource dataSource; // 封装其它的映射文件中属性 private Map<String, Mapper> mappers = new HashMap<>(); public Configuration() { // 加载sqlMapConfig.xml中的数据库链接参数 loadSqlMapConfig(); } private void loadSqlMapConfig() { // 得到输入流 InputStream in = Configuration.class.getResourceAsStream("/sqlMapConfig.xml"); // DOM解析 // 得到文档对象 SAXReader saxReader = new SAXReader(); try { Document document = saxReader.read(in); // 加载数据库连接参数 // 获得数据库连接字符串:driver Element driverElement = (Element) document.selectSingleNode("//property[@name='driver']"); driver = driverElement.attributeValue("value"); // 获得数据库连接字符串:url Element urlElement = (Element) document.selectSingleNode("//property[@name='url']"); url = urlElement.attributeValue("value"); // 获得数据库连接字符串:username Element usernameElement = (Element) document.selectSingleNode("//property[@name='username']"); username = usernameElement.attributeValue("value"); // 获得数据库连接字符串:password Element passwordElement = (Element) document.selectSingleNode("//property[@name='username']"); password = passwordElement.attributeValue("value"); // 创建数据源 DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(username); ds.setPassword(password); this.dataSource = ds; // TODO:加载接口映射文件 } catch (DocumentException e) { e.printStackTrace(); } } }
小结
loadSqlMapConfig()方法做了什么事情?
解析核心配置文件的数据,保存到Configuration的成员成员变量中,解析了
dirver,url, username,password,创建了连接池
自定义mybatis:编写Configuration解析Mapper实体类映射文件
目标
解析UserMapper.xml并且封装到Mapper类中
分析
实现步骤
作用:进一步解析接口映射XML文件,给mappers属性赋值
-
读取mapper中的resource属性值
-
解析resource对应的XML文件,得到namespace,id,resultType,sql的值
-
封装成Mapper对象,保存到Map集合中
代码
package com.itheima.mybatis; import com.alibaba.druid.pool.DruidDataSource; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.Node; import org.dom4j.io.SAXReader; import javax.sql.DataSource; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; /* 1.封装sqlMapConfig.xml配置信息 2.得到数据源 3.加载UserMapper.xml配置信息 */ public class Configuration { // 数据源的四个属性 private String driver; private String url; private String username; private String password; // 数据源 private DataSource dataSource; // 封装其它的映射文件中属性 private Map<String, Mapper> mappers = new HashMap<>(); public Configuration() { // 加载sqlMapConfig.xml中的数据库链接参数 loadSqlMapConfig(); } private void loadSqlMapConfig() { // 得到输入流 InputStream in = Configuration.class.getResourceAsStream("/sqlMapConfig.xml"); // DOM解析 // 得到文档对象 SAXReader saxReader = new SAXReader(); try { Document document = saxReader.read(in); // 加载数据库连接参数 // 获得数据库连接字符串:driver Element driverElement = (Element) document.selectSingleNode("//property[@name='driver']"); driver = driverElement.attributeValue("value"); // 获得数据库连接字符串:url Element urlElement = (Element) document.selectSingleNode("//property[@name='url']"); url = urlElement.attributeValue("value"); // 获得数据库连接字符串:username Element usernameElement = (Element) document.selectSingleNode("//property[@name='username']"); username = usernameElement.attributeValue("value"); // 获得数据库连接字符串:password Element passwordElement = (Element) document.selectSingleNode("//property[@name='username']"); password = passwordElement.attributeValue("value"); // 创建数据源 DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(username); ds.setPassword(password); this.dataSource = ds; // 加载接口映射文件 List<Node> nodes = document.selectNodes("//mapper"); for (Node node : nodes) { Element mapperElement = (Element) node; // com/itheima/dao/UserMapper.xml String resource = mapperElement.attributeValue("resource"); InputStream mapperIn = Configuration.class.getResourceAsStream("/" + resource); // DOM解析 SAXReader reader = new SAXReader(); Document mapperDocument = reader.read(mapperIn); // rootElement:<mapper namespace="com.itheima.dao.UserMapper"> Element rootElement = mapperDocument.getRootElement(); String namespace = rootElement.attributeValue("namespace"); /* <select id="findAllUsers" resultType="com.itheima.entity.User"> select * from user; </select>*/ Element select = rootElement.element("select"); // 获得id属性值 String id = select.attributeValue("id"); // 获得返回值类型 String resultType = select.attributeValue("resultType"); // 获得标签体内容:sql语句字符串 String sql = select.getTextTrim(); // 创建Mapper对象 Mapper mapper = new Mapper(namespace, id, resultType, sql); // 将Mapper对象添加到集合mappers中 mappers.put(namespace + "." + id, mapper); } } catch (DocumentException e) { e.printStackTrace(); } } // 省略getter/setter }
小结
-
解析UserMapper.xml文件,封装成Mapper对象,其中键和值分别是什么?
键:接口类全名+"." + “方法名”
值:Mapper对象,封装了4个属性:namespace,id,resultType,sql
自定义mybatis:封装查询的结果集
目标
- 使用JDBC从数据库中查询数据
- 使用反射来实例化查询结果对象,并且封装一条记录到对象中,添加对象到集合中
步骤
-
通过连接池得到连接对象
-
使用JDBC访问数据库执行SQL语句
-
处理结果集中的每条记录
-
使用反射将每条记录封装成一个对象
-
关闭资源
代码
package com.itheima.mybatis; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.sql.*; import java.util.ArrayList; import java.util.List; // Mapper的代理对象是用来执行SQL语句的 public class MyInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 返回查询数据 // 思考:在这个方法中,需要做什么? // 1.通过数据源得到连接对象 // 2.得到要执行的 sql 语句 // 3.执行数据库操作,封装结果集并且返回 String methodName = method.getName(); String className = method.getDeclaringClass().getName(); String allName = className + "." + methodName; System.out.println("allName = " + allName); // allName = com.itheima.dao.UserMapper.findAllUsers // 1.通过数据源得到连接对象 Configuration configuration = new Configuration(); // 2.得到要执行的sql语句 Mapper mapper = configuration.getMappers().get(allName); // 得到返回值类型 String resultType = mapper.getResultType(); Class cls = Class.forName(resultType); // 得到sql语句 String sql = mapper.getSql(); Connection conn = configuration.getDataSource().getConnection(); // 3.执行数据库操作,封装结果集并且返回 return query(conn, sql, cls); } // 执行sql语句,去数据库查询数据 public <T> List<T> query(Connection conn, String sql, Class<T> returnType) { // 创建集合存储查询后的对象 ArrayList<T> list = new ArrayList<>(); PreparedStatement pstmt = null; ResultSet rs = null; try { // 执行sql语句 pstmt = conn.prepareStatement(sql); rs = pstmt.executeQuery(); // 得到查询结果的元信息,包括字段个数 ResultSetMetaData metaData = rs.getMetaData(); // 循环获取每条记录,转成对象 while (rs.next()) { // 反射创建对象 T obj = returnType.getConstructor().newInstance(); // 得到字段的个数 for (int i = 1; i <= metaData.getColumnCount(); i++) { // 获取字段的名称 String columnName = metaData.getColumnName(i); // 通过字段的名称获取字段的值 Object value = rs.getObject(columnName); // 通过反射得到类中的成员变量 Field field = returnType.getDeclaredField(columnName); field.setAccessible(true); // 给对象的这个成员变量设置值 field.set(obj, value); } // 将一个转换好的对象添加到集合中 list.add(obj); } } catch (Exception e) { e.printStackTrace(); } finally { close(conn, pstmt, rs); } // 返回集合 return list; } /** * 关闭连接对象,语句对象,结果集对象 */ public void close(Connection connection, Statement stmt, ResultSet rs) { // 结果集不为空则关闭 if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } // 不为空则关闭 if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } // 连接对象不为空,则关闭 if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
小结
- Mybatis实现自定义的typehandler三步曲
- Mybatis自定义ResultMap使用总结
- Mybatis初窥:使用resultMap自定义高级映射规则
- Mybatis中ObjectFactory自定义实现
- Mybatis 自定义SqlSessionFactoryBean扫描通配符typeAliasesPackage
- Mybatis传自定义参数问题
- 基于SpringMVC、MyBatis、FreeMarker架构实战CMS大型门户网站(自定义模板)
- SpringCloud SpringBoot mybatis 分布式微服务(二十)Spring Boot 自定义配置文件
- mybatis的自定义主键生成策略
- MyBatis 缓存机制深度解剖 / 自定义二级缓存
- mybatis-枚举类型的typeHandler&自定义枚举类型typeHandler
- mybatis自定义typeHandler
- 在springMVC+mybatis+Ajax+JSON+jQuery easyui的项目中,自定义全局异常处理器
- mybatis-generator扩展教程系列 -- 自定义generatorConfig.xml参数
- Mybatis实战之自定义TypeHandler处理枚举
- MyBatis 查询映射自定义枚举
- springboot + mybatis自定义枚举类型转换
- 微信自定义菜单 快递接口 SpringMVC mybatis redis shiro ehcache websocket
- Mybatis实现自定义类型转换器TypeHandler的方法
- Mybatis中的typehandler 自定义数据转换储存读取