自定义 Mybatis 框架
分析流程

1、 引入dom4j
<dependencies>
<!--<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
2、引入解析xml的工具类
3、引入executor类 负责执行sql语句并且封装结果集
/**
* @author 黑马程序员
* @Company http://www.ithiema.com
* 负责执行SQL语句,并且封装结果集 返回list集合
*/
public class Executor {
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.itheima.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);
}
}
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、获取数据库连接
public class DataSourceUtil {
private static Connection conn;
public static Connection getConnection(Configuration cfg) throws ClassNotFoundException {
Class.forName(cfg.getDriver());
try {
conn=DriverManager.getConnection(cfg.getUrl(),cfg.getUsername(),cfg.getPassword());
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
}
5、编写SqlMapConfig.xml
<?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"/>
<!-- 配置数据源 (连接池) -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件位置 映射配置文件是指dao独立的配置文件 -->
<mappers>
<mapper resource="com/itheim/dao/IUserDao.xml"/>
<!--<mapper class="com.itheim.dao.IUserDao"/>-->
</mappers>
</configuration>
**注意**:
此处我们直接使用的是 mybatis 的配置文件,但是由于我们没有使用 mybatis 的 jar 包,所以要把配
置文件的约束删掉否则会报错(如果电脑能接入互联网,不删也行)
6、编写读取配置文件类
public class Resources {
/**
* 用于加载 xml 文件,并且得到一个流对象
* @param xmlPath
* @return
* 在实际开发中读取配置文件:
* 第一:使用类加载器。但是有要求:a 文件不能过大。 b 文件必须在类路径下(classpath)
* 第二:使用 ServletContext 的 getRealPath()
*/
public static InputStream getResourceAsStream(String xmlPath){
return Resources.class.getClassLoader().getResourceAsStream(xmlPath);
}
}
7、编写mapper类
/**
*
* <p>Title: Mapper</p>
* <p>Description: 用于封装查询时的必要信息:要执行的 SQL 语句和实体类的全限定类名</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class Mapper {
private String QueryString;//查询语句
private String ResultType;//结果类型
public String getQueryString() {
return QueryString;
}
public void setQueryString(String queryString) {
QueryString = queryString;
}
public String getResultType() {
return ResultType;
}
public void setResultType(String resultType) {
ResultType = resultType;
}
@Override
public String toString() {
return "Mapper{" +
"QueryString='" + QueryString + '\'' +
", ResultType='" + ResultType + '\'' +
'}';
}
}
8、编写 Configuration 配置类
/**
* 核心配置类
* 1.数据库信息
* 2.sql 的 map 集合
*/
public class Configuration {
private String Driver;
private String Url;
private String Username;
private String Password;
private Map<String, Mapper> mappers=new HashMap<String,Mapper>();
@Override
public String toString() {
return "Configuration{" +
"Driver='" + Driver + '\'' +
", Url='" + Url + '\'' +
", Username='" + Username + '\'' +
", Password='" + Password + '\'' +
", Mappers=" + mappers +
'}';
}
public String getDriver() {
return Driver;
}
public void setDriver(String driver) {
Driver = driver;
}
public String getUrl() {
return Url;
}
public void setUrl(String url) {
Url = url;
}
public String getUsername() {
return Username;
}
public void setUsername(String username) {
Username = username;
}
public String getPassword() {
return Password;
}
public void setPassword(String password) {
Password = password;
}
public Map<String, Mapper> getMappers() {
return mappers;
}
public void setMappers(Map<String, Mapper> mappers) {
this.mappers.putAll(mappers);
}
}
9、编写 User 实体类
/**
* 封装信息
*/
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
基于 XML 的自定义 mybatis 框架
1、编写持久层接口和 IUserDao.xml
public interface IUserDao {
/**
* 查询所有用户
* @return
*/
public List<User> findAll();
}
<?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.itheim.dao.IUserDao">
<select id="findAll" resultType="com.itheim.domain.User"> <!--指定包装类型 -->
select * from user;
</select>
</mapper>
**注意**:
此处我们使用的也是 mybatis 的配置文件,所以也要把约束删除了
2、编写构建者类
/**
*
* <p>Title: SqlSessionFactoryBuilder</p>
* <p>Description: 用于构建 SqlSessionFactory 的</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream in){
Configuration configuration = XMLConfigBuilder.loadConfiguration(in);
return new DefaultSqlSessionFactory(configuration);
}
}
3、编写 SqlSessionFactory 接口和实现类
/**
*
* <p>Title: SqlSessionFactory</p>
* <p>Description: SqlSessionFactory 的接口</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public interface SqlSessionFactory {
public SqlSession openSession() throws ClassNotFoundException;
}
/**
*
* <p>Title: DefaultSqlSessionFactory</p>
* <p>Description:SqlSessionFactory 的默认实现 </p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private Configuration cfg=null;
public DefaultSqlSessionFactory(Configuration cfg) {
this.cfg = cfg;
}
@Override
public SqlSession openSession() throws ClassNotFoundException {
return new DefaultSqlSession(cfg);
}
}
4、编写 SqlSession 接口和实现类
public interface SqlSession {
/**
* 创建dao接口的代理对象
* @param daoClass
* @param <T>
* @return
*/
public <T> T getMapper(Class<T> daoClass);
/**
* 关闭资源
*/
public void close();
}
/**
*
* <p>Title: DefaultSqlSession</p>
* <p>Description: SqlSession 的具体实现</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class DefaultSqlSession implements SqlSession {
private Configuration cfg;
private Connection conn;
public DefaultSqlSession(Configuration cfg) throws ClassNotFoundException {
this.cfg = cfg;
conn = DataSourceUtil.getConnection(cfg);
}
@Override
public <T> T getMapper(Class<T> daoClass) {
return (T)Proxy.newProxyInstance(daoClass.getClassLoader(),new Class[]{daoClass},new MapperProxy(cfg.getMappers(),conn));
}
@Override
public void close() {
if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
动态代理
/**
* 动态代理:
* 涉及的类:Proxy
* 使用的方法:newProxyInstance
* 方法的参数:
* ClassLoader:和被代理对象使用相同的类加载器,通常都是固定的
* Class[]:代理对象和被代理对象要求有相同的行为。(具有相同的方法)
* InvocationHandler:如何代理。需要我们自己提供的增强部分的代码
*/
public class MapperProxy implements InvocationHandler {
private Map<String,Mapper> mappers;
private Connection conn;
public MapperProxy(Map<String, Mapper> mappers, Connection conn) {
this.mappers=mappers;
this.conn=conn;
}
/**
* 用于对方法增强 调用selectlist
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName(); //获取方法名
String className = method.getDeclaringClass().getName();//获取方法所在类的名
//组合key
String key=className+"."+methodName;
System.out.println(key);
//4.使用 key 取出 mapper
Mapper mapper = mappers.get(key);
Executor executor = new Executor();
//5.判断是否有mapper
if(mapper == null){
throw new IllegalArgumentException("传入的参数有误");
}
return executor.selectList(mapper,conn);
}
}
5、运行测试类
public class UserDao{
public static void main(String[] args) throws IOException, ClassNotFoundException {
//读取配置文件
InputStream in = Resources.getResourceAsStream("mybatisConfig.xml");
//创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//使用工厂生产Sqlsession对象
SqlSession session = factory.openSession();
//使用Sqlsession创建Dao接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
//实现类的方法
// IUserDao userDao=new UserDaoImp(factory);
List<User> users=userDao.findAll();
for (User user : users) {
System.out.println(user);
}
//释放资源
// session.close();
in.close();
}
}
基于注解方式定义 Mybatis 框架
自定义@Select 注解
/**
* <p>Title: Select</p>
* <p>Description: 自定义查询注解</p>
* <p>Company: http://www.itheima.com/ </p>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Select {
String value();
}
自定义 Mybatis 框架的更多相关文章
- 自定义Mybatis框架
项目结构: https://files-cdn.cnblogs.com/files/mkl7/ownMybatis.zip 1. 创建maven工程并引入坐标: <?xml versi ...
- 阶段3 1.Mybatis_03.自定义Mybatis框架_3.自定义mybatis的编码-根据测试类中缺少的创建接口和类
先认识一下这几个类.Resources是一个class SqlSessionFactoryBuilder 创建新项目 复制相关的依赖 复制之前的代码 复制到当前项目的src下 把Mybits的依赖删除 ...
- 阶段3 1.Mybatis_04.自定义Mybatis框架基于注解开发_3 基于注解的自定义再分析
这里只需要 一是连接数据库的 二是映射的 注解是class的方式 dom4j技术获取xml的数据,这是xml的方式获取的下面几个关键的点 注解的方式回去dao类里面的几个主要的信息 User黄色的部 ...
- 阶段3 1.Mybatis_04.自定义Mybatis框架基于注解开发_2 回顾自定义mybatis的流程分析
- 阶段3 1.Mybatis_04.自定义Mybatis框架基于注解开发_1 今日课程内容介绍
- 阶段3 1.Mybatis_03.自定义Mybatis框架_7.自定义Mybatis的编码-实现基于注解配置的查询所有
注解的方式,这里进行修改.上面注释的是原来xml的方式. 在dao类里面加上注解 创建注解类 声明注解的生命周期为Runntime 改变注解出现的位置,在Mehtod方法上 写完之后这里就不报错了. ...
- 阶段3 1.Mybatis_03.自定义Mybatis框架_6.自定义Mybatis的编码-实现基于XML的查询所有操作
接下来就可以写创建代理对象的方法了 类加载器,代理谁,就用谁的加载器,因为这里用daoInterfaceClass.getClassLoader() 第二个代理谁就要和谁有相同的接口,daoInter ...
- 阶段3 1.Mybatis_03.自定义Mybatis框架_5.自定义Mybatis的编码-创建两个默认实现类并分析类之间的关系
把XMLConfigBuilder的包名补全 这样我们就可以调用里面的loadConfiguration方法了 创建工厂实现类 实现SqlSessionFactory的接口 实现接口里面的方法 把cf ...
- 阶段3 1.Mybatis_03.自定义Mybatis框架_4.自定义mybatis的编码-解析XML的工具类介绍
导入xml操作的类和用到的相关包 创建util包,然后把提供好的XMLConfigBuilder.java文件复制3过来 复制过来,里面用到了很多dom4j的东西 打开pom.xml 输入depend ...
随机推荐
- 选择困难症必看!云服务器如何选择操作系统,Windows和Linux哪个更好?
在购买云服务器时,会有一个必选的配置,就是操作系统的选择,如何选择操作系统?操作系统选择错了怎么办?这是不少用户会遇到的问题,今天我们就来教大家如何选择操作系统,以及操作系统选择错了,该怎么切换. W ...
- 数据可视化之powerBI入门(八)PowerQuery学习:认识M函数
https://zhuanlan.zhihu.com/p/64148928 前面我们学习PQ的时候都是用鼠标操作,虽然通过这些操作能完成大部分的数据处理,但是毕竟还有些复杂的工作是处理不了的,如果想彻 ...
- 目录(Python基础)
Python之介绍.基本语法.流程控制 Python之列表.字典.集合 Python之函数.递归.内置函数 Python之迭代器.装饰器.软件开发规范 Python之常用模块学习(一) Python之 ...
- Python数据可视化:画饼状图、折线图、圈图
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. from math import pi import matplotlib ...
- 009.Nginx缓存及配置
一 浏览器缓存 1.1 缓存概述 缓存对于Web至关重要,尤其对于大型高负载Web站点.Nginx缓存可作为性能优化的一个重要手段,可以极大减轻后端服务器的负载.通常对于静态资源,即较少经常更新的资源 ...
- As 布局文件太多很乱的问题
//添加自定义文件整理文件夹的方法,没有之一在build.gradle(Module: app)里加入布局需要放入的路径代码>>>>>> sourceSets { ...
- html 转义和反转义
public static void main(String[] args) {// String html = "<img style=\"width: 100%; hei ...
- Oracle Database Tools
The following are some products, tools, and utilities you can use to achieve your goals as a databas ...
- 不懂DevOps!他在升职加薪的那天下午,提出了离职
不久前我们一个已毕业的学员向班主任老师分享了前几天他遇到的一件事: 一个许久未联系他的朋友突然打电话给他,寒暄了几句后突然说,想来北京找工作,问能不能帮忙给介绍一些工作. 在接下来的通话中,我们学员了 ...
- git push到远程新分支
获取远程代码并在本地切换到一个新分支修改后,想要 push 到远端与原来不同的新分支,可以使用下面的命令实现: git push origin 本地分支:远端希望创建的分支 上面的本地分支 是基于拉取 ...