MyBatis(自定义框架)

项目三层架构介绍

三层架构有哪些

  • 表示层
  • 业务层
  • 数据访问层

三层架构好处

  1. 降低层与层之间的耦合性。
  2. 提高软件的可维护性。
  3. 提升软件的可扩展性。

框架概述

什么是框架

框架是别人写好的代码,是对常见功能的封装,是一个半成品。在框架的基础上开发出成品的软件。

假如要造一辆马车,在没有零件的情况下,需要自己去伐木,去把木头做成木板,木棍,然后做成轮子,门,等部件,然后组装起来,很麻烦。框架就相当于现成的轮子,门等部件。只需要拿过来使用即可。

常见框架如:Spring,SpringMVC,Mybatis,JdbcTemplate,Bootstrap等等。

框架解决的问题

提升了开发效率

企业项目中使用框架,程序员不再需要重复造轮子,只需要专注实现业务需求,提升了开发效率。

提升了系统稳定性

一个成熟的框架,经过了在众多企业项目中的验证使用,稳定性有保障。

分层开发下常见框架

ssm就是3个框架:Spring、SpringMVC、MyBatis

持久层框架

MyBatis是一款优秀的持久层框架,MyBatis 避免了几乎所有的 JDBC代码和手动设置参数以及获取结果集。

关于持久层的框架,还有一个封装程度更高的框架(hibernate)。该框架相对比较重量级,以及其它各个方面的原因,目前流行程度下降了很多,企业项目中用的越来越少了。

表现层框架

SpringMVC是一种基于Java,实现了Web MVC设计模式,将Web层进行解耦。SpringMVC可以简化我们日常Web开发。

整合的框架

Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。让开发变的更简单。

mybatis框架介绍和下载

mybatis框架介绍

mybatis是Apache软件基金会下的一个开源项目,前身是iBatis框架。2010年这个项目改名为mybatis。是一个持久层框架

mybatis的优点

  1. 简单易学:不需要关注JDBC的资源获取和释放,配置几个SQL映射文件即可。
  2. 解除SQL与程序代码的耦合:SQL语句和代码分离,提高了可维护性。

官方网站

http://www.mybatis.org/mybatis-3/

框架包下载

连接到github地址:https://github.com/mybatis/mybatis-3/releases

mybatis入门示例(环境的搭建)

需求

利用mybatis框架,从MySQL中查询所有的用户

用户表SQL

CREATE TABLE USER (
  id INT PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(20) NOT NULL,
  birthday DATE,
  sex CHAR(1) DEFAULT '男',
  address VARCHAR(50)
);

INSERT INTO USER VALUES (NULL, '小明','1996-10-24','男','香港');

SELECT * FROM USER;

mybatis开发步骤

  1. 创建新模块
  2. 加入mybatis和数据库驱动jar包
  3. 编写用户实体类(User)
  4. 准备核心配置文件:sqlMapConfig.xml
  5. 编写用户 dao 接口(UserMapper)
  6. 编写用户 dao 接口映射文件(UserMapper.xml)
  7. 编写测试代码

环境搭建

  1. 创建模块:mybatis_demo

  2. 加入mybatis相关jar包:在模块下新建lib文件夹,复制mybatis框架jar包到lib文件夹下
    mybatis 框架包
    mysql 数据库驱动包
    log4j 日志包

  3. 编写用户实体类(User)

    /**
     用户实体类对象 */
    public class User {
    
        private Integer id;
        private String username;
        private Date birthday;
        private String sex;
        private String address;
    
    	// 省略构造方法/getter/setter/toString
    }
    

配置文件

复制log4j.properties到 src 下

### 设置Logger输出级别和输出目的地 ###
log4j.rootLogger=debug, stdout

### 把日志信息输出到控制台 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout

Mybatis配置文件分类

mybatis为了灵活,将数据库相关数据和SQL语句写到XML配置文件中。

  1. 核心配置文件:配置数据库的连接url,账号,密码,一般核心配置文件的名称:sqlMapConfig.xml
  2. 接口的映射文件:配置要执行的SQL语句,一般名称: XxxMapper.xml

核心配置文件sqlMapConfig.xml

复制mybatis框架模板\sqlMapConfig.xmlsrc下,说明:它是mybatis框架的核心配置文件,mybatis就是靠这些信息来运行的。

<?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>
    <!--可以配置多个环境,可以访问不同种类的数据库:mysql, oracle-->
    <environments default="default">
        <!-- 其中的一个配置环境,这个配置方案的唯一标识 -->
        <environment id="default">
            <!--
            指定事务管理器的类型:
            JDBC:使用JDBC来管理事务
            -->
            <transactionManager type="JDBC"/>
            <!--
            数据源的类型:
            1. POOLED:使用mybatis创建的连接池
            2. UNPOOLED:不使用连接池,每次都创建和关闭连接
            -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <!--加载接口映射文件-->
    <mappers>
        <mapper resource="接口映射文件位置"/>
    </mappers>
</configuration>

接口映射文件UserMapper.xml

UserMapper接口的创建

创建com.xxx.dao包,创建UserMapper接口。编写查询所有用户的方法:List<User> findAllUsers();

/**
 定义DAO中方法
 */
public interface UserMapper {
    /**
     查询所有的用户
     */
    List<User> findAllUsers();
}

UserMapper.xml映射文件

在com.xxx.dao包中创建UserMapper.xml映射文件

<?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">

<!--namespace属性:指定DAO接口中类全名-->
<mapper namespace="com.xxx.dao.UserMapper">
    <!--
    id:接口中方法的名字
    resultType: 返回的数据类型,如果是集合类型,这里应该指定集合中元素的类型全名
    文本:SQL语句
    -->
    <select id="findAllUsers" resultType="com.xxx.entity.User">
        select * from user;
    </select>
</mapper>

核心配置文件sqlMapConfig.xml

在src/sqlMapConfig.xml添加UserMapper.xml的映射

<!--加载其它的实体映射文件-->
<mappers>
    <!--指定要加载的映射文件-->
    <mapper resource="com/xxx/dao/UserMapper.xml"/>
</mappers>

测试类

三大对象

  1. SqlSessionFactoryBuilder:负责构建SqlSessionFactory
  2. SqlSessionFactory:创建SqlSession实例的工厂
  3. SqlSession:用于执行SQL操作的对象

编写代码流程

  1. 创建SqlSessionFactoryBuilder对象
  2. 得到会话工厂SqlSessionFactory类
  3. 得到SqlSession对象
  4. 通过SqlSession对象得到Mapper接口的代理对象
  5. Mapper接口的代理对象执行数据库的查询操作

示例代码

public class TestUserMapper {

    public static void main(String[] args) throws IOException {
        // 1.得到核心配置文件的输入流
        InputStream in = TestMybatis.class.getResourceAsStream("/sqlMapConfig.xml");

        // 2.创建会话工厂建造类
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

        // 3.创建会话工厂
        SqlSessionFactory factory = builder.build(inputStream);

        // 4.通过会话工厂得到会话对象
        SqlSession session = factory.openSession();

        // 5.由会话对象得到DAO接口的对象,由mybatis生成接口的代理对象。
        UserMapper userMapper = session.getMapper(UserMapper.class);

        // 6.实现CRUD
        List<User> userList = userMapper.findAllUsers();
        for (User user : userList) {
            System.out.println(user);
        }

        // 7.释放资源
        session.close();
    }
}

注:UserMapper的对象是由mybatis生成的接口代理对象

自定义mybatis

编写SqlSession类

  1. 新建com.xxx.mybatis
  2. com.xxx.mybatis创建SqlSession类
  3. SqlSession中编写getMapper获取Mapper的代理对象
SqlSession sqlSession = new SqlSession();
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;
    }
}

解析sqlMapConfig.xml核心配置文件

实现步骤

  1. 使用dom4j解析sqlMapConfig.xml文件
  2. 获取数据库相关信息
  3. 创建Druid连接池

Configuration代码

public class Configuration {
    // 数据源
    private DataSource dataSource;

    public Configuration() {
        // 加载sqlMapConfig.xml中的数据库链接参数
        // 得到输入流
        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();
        }
    }
}

设计Mapper类

步骤

  1. 创建Mapper实体类:包含4个属性:namespace,id,resultType,sql
  2. 生成get和set方法

代码

/**
 用来封装映射文件的实体类
 一个Mapper对象代表一条要操作的查询语句对象
 */
public class Mapper {
    private String namespace;  //接口类全名
    private String id;   //接口中方法名
    private String resultType;  //封装的数据类型
    private String sql;   //要执行的SQL语句

   	// 省略getter/setter
}

写Configuration解析Mapper实体类映射文件

实现步骤

  1. 读取mapper中的resource属性值

  2. 解析resource对应的XML文件,得到namespace,id,resultType,sql的值

  3. 封装成Mapper对象,保存到Map集合中

示例代码

public class Configuration {
    // 数据源
    private DataSource dataSource;

    // 封装其它的映射文件中属性
    private Map<String, Mapper> mappers = new HashMap<>();

    public Configuration() {
        // 加载sqlMapConfig.xml中的数据库链接参数
        // 得到输入流
        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;
            
            // 加载接口映射文件
            Element mapperElement = (Element) document.selectSingleNode("//mapper[@resource]");
           
            // com/xxx/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.xxx.dao.UserMapper">
            Element rootElement = mapperDocument.getRootElement();
            String namespace = rootElement.attributeValue("namespace");

            /* <select id="findAllUsers" resultType="com.xxx.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
}

封装查询的结果集

步骤

  1. 通过连接池得到连接对象

  2. 使用JDBC访问数据库执行SQL语句

  3. 处理结果集中的每条记录

  4. 使用反射将每条记录封装成一个对象

  5. 关闭资源

代码

// Mapper的代理对象是用来执行SQL语句的
public class MyInvocationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 去数据库查询数据返回
        String methodName = method.getName();
        // 得到这个方法声明是的Class对象
        String className = method.getDeclaringClass().getName();// 得到类全名
        String allName = className + "." + methodName;
        System.out.println("allName = " + allName);

        // 通过键找到值,mapper对象
        Configuration configuration = new Configuration();
        Mapper mapper = configuration.getMappers().get(allName);

        String sql = mapper.getSql();
        String resultTypeString = mapper.getResultType(); // com.xxx.entity.User
        Class<?> resultType = Class.forName(resultTypeString);

        // 执行SQL
        // 1.通过连接池得到连接对象
        DruidDataSource dds = configuration.getDds();
        Connection conn = dds.getConnection();
        PreparedStatement pstmt = conn.prepareStatement(sql);
        // 2.使用JDBC访问数据库执行SQL语句
        ResultSet rs = pstmt.executeQuery();
        // 3.处理结果集中的每条记录
        ArrayList list = new ArrayList();

        // 4.使用反射将每条记录封装成一个对象
        while (rs.next()) {
            // 1.创建对象
            Object user = resultType.newInstance();
            Field[] fields = resultType.getDeclaredFields();

            for (Field field : fields) {
                field.setAccessible(true);
                // 获取成员变量名
                String name = field.getName();
                // 获取字段值
                Object value = rs.getObject(name);
                field.set(user, value);
            }
            // 5.将对象添加到集合中
            list.add(user);
        }

        // 6.关闭资源
        rs.close();
        pstmt.close();
        conn.close();
        return list;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值