自定义 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 ...
随机推荐
- python 网络爬虫报错“UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position”解决方案
Python3.x爬虫, 发现报错“UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1:invalid sta ...
- python 装饰器(三):装饰器实例(一)
示例 7-15 定义了一个装饰器,它会在每次调用被装饰的函数时计时,然后把经过的时间.传入的参数和调用的结果打印出来.示例 7-15 一个简单的装饰器,输出函数的运行时间 import time de ...
- 数据可视化之DAX篇(二十一)连接表的几个DAX函数,一次全掌握
https://zhuanlan.zhihu.com/p/67015995 编写DAX代码进行业务分析时,经常会用到表与表之间的连接计算,比如在之前的产品关联分析一文中(如何用Power BI分析产品 ...
- Python 之 Restful API设计规范
理解RESTful架构 Restful API设计指南 理解RESTful架构 越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这种"互联网软件"采用客户端/服务器模式 ...
- Ethical Hacking - NETWORK PENETRATION TESTING(8)
WEP Cracking Basic case Run airdump-ng to log all traffic from the target network. airodump-ng --cha ...
- 【C#】根据开始时间和结束时间筛选存在的信息
背景 业务需求中,需要根绝开始时间和结束时间筛选一段时间内的任务存在个数. 示例图片 根据开始时间 9:00到 结束时间11:00 筛选信息 总共有这么四种情况可能出现 插入测试数据 CREATE T ...
- less : 解决升级后报错的问题
vue2项目. 上版本. { "name": "xxx", "version": "1.0.0", "desc ...
- NameBeta - 多家比价以节省咱的域名注册成本
共收录 1584 种顶级域名,汇集互联网上 29 家知名域名注册商,每日更新价格信息 有的域名还可以查出到期时间点我前往官网 NameSilo1美元优惠码:whatz
- STL源码剖析:关联式容器
AVL树 AVL树定义:红黑树是一颗二叉搜索树,特别的是一棵保持高度平衡的二叉搜索树 AVL树特点: 每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1 AVL树插入: 说明:新增节点的平衡因子 ...
- JAXB XML生成CDATA类型的节点
试了好久才找到一个解决办法,我是用的JAXB的,如果你们也是用JAXB那么可以直接借鉴此方法,别的方式你们自行测试吧 第一步:新增一个适配器类 package com.message.util; im ...