MyBatisJava 持久层框架入门与实践 | 学习笔记

Posted Fxtack

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatisJava 持久层框架入门与实践 | 学习笔记相关的知识,希望对你有一定的参考价值。

Java 持久层框架入门与实践 | 学习笔记


导读

本篇文章将带领读者学习 MyBatis 框架。开始文章简述 MyBatis。之后将手把手的搭建一个简单的 MyBatis 框架的测试,先跑起来。随后将基于该测试讲解 MyBatis 框架中重要的几个配置文件。然后对于数据库常用的增、删、查、改操作在 MyBatis 中的进行实现,并讨论一些 Java 中集合在 MyBatis 中的配置与应用。剩下的时间将讲解一些细节而常用的知识。

文中代码中嵌有大量的注释,这些注释对于理解和学习 MyBatis 有巨大的作用,请不要因为页面显示的字体颜色不清晰而忽略掉它们。

文中的测试与运行环境为:Java 8,IDEA 2020,Maven3.6.1,MyBatis 3.4.6,mysql 8.0.15,Windows 10。
请格外留意 Mysql 的版本,低于 Mysql 8 的 mybatis-config.xml 配置将与本文中的配置不同


文章目录


一. MyBatis 简介

  1. MyBatis 是支持定制化 SQL、存储过程、高级映射的优秀持久层框架
  2. MyBatis 避免了几乎所有的 JDBC 代码或手动设置参数以及获取结果集
  3. MyBatis 可以使用简单的 XML 或注释用于配置和原始映射,将接口和 Java 的 POJO(Plain Old Java Objects,即普通的 Java 对象)映射成数据库中的记录
  4. MyBatis 是一个半自动的 ORM (Object Relation Mapping)框架

二. 现有的持久化技术

  1. JDBC
    • SQL 夹在 Java 代码里,耦合度高导致硬编码内伤
    • 维护不易切实际开发需求中 SQL 有变化,修改频繁
  2. Hibernate 和 JPA
    • 长难 SQL,对于 Hibernate 而言处理也不容易
    • 内部自动生成 SQL,不容易做特殊优化
    • 基于全映射的自动框架,大量字段的 POJO 进行部分映射比较困难。导致数据库性能下降
  3. MyBatis
    • 对于开发人员而言,核心 SQL 依然需要自己优化
    • SQL 和 Java 编码分开,功能边界清晰,一个专注业务,一个专注数据,更好的解耦合

三. MyBatis 项目搭建过程

  1. 添加 Maven 依赖(或者导入 jar 包)

    <dependency>
    	<groupId>org.mybatis</groupId>
    	<artifactId>mybatis</artifactId>
    	<version>3.4.6</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
             <version>8.0.15</version>
     </dependency>
    

    注意搭建 MyBatis 项目,需要另外添加 JDBC 依赖(或导入 JDBC 的 jar 包),本文中使用 Mysql 的 JDBC

  2. 创建 MyBatis 的核心(全局)配置文件 mybatis-config.xml

    该配置文件在 Maven 项目中在 resource 目录下 配置以MySQL为数据库为例

    <?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="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <!--配置数据库的驱动-->
                    <!--注意,此处的配置只适用 Mysql 8及以上的版本-->
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <!--配置数据库的URL-->
                    <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatistest?useUnicode=true&amp;characterEncoding=UTF-8&amp;userSSL=false&amp;serverTimezone=GMT%2B8"/>
                    <!--配置数据库的登录用户名-->
                    <property name="username" value="root"/>
                    <!--配置数据库的登录用密码-->
                    <property name="password" value="1244875112"/>
                </dataSource>
            </environment>
        </environments>
        <mappers>
            <!--设置映射文件,映射文件将在下面介绍-->
            <mapper resource="UserMapper.xml"/>
            <!--可以使用<package>标签引导映射到包目录下的Mapper文件,但要求为该包必须在全局配置文件同包目录下-->
        </mappers>
    </configuration>
    
  3. 创建映射文件 XxxMapper.xml,并配置

    在 MySQL 数据库简单创建一个测试数据库:

    User表:

    uiduserNamepasswordagesex
    1admin12345623

    在 Maven 项目的 resources 目录下创建映射文件 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">
    <!--命名空间标签填入映射接口的全类名-->
    <mapper namespace="org.example.Mapper.UserMapper">
        <!--操作标签,uid为映射接口的抽象方法名,resultType为该抽象方法的返回值类型,需书写全类名-->
        <!--org.example.Bean.User 为测试的实体类,在下文中将列出-->
        <select id="getUserByUid" resultType="org.example.Bean.User">
            <!--具体操作的SQL语句-->
            select * from User where uid = #id
        </select>
    </mapper>
    
  4. 创建 mapper 接口,实现两个绑定

    UserMapper.xml 实现了两个绑定

    • 接口全限定名要和映射文件的命名空间( 标签的 namespace 属性)保持一致
    • 接口中的方法名和 SQL 语句的 id ( 标签的属性)保持一致,返回值与 resultType( 标签的属性) 一致

    UserMapper.xml 中涉及到的类资源与映射接口如下

    User.java 实体类

    package org.example;
       
    	public class User 
          
           private Integer uid;
           private String userName;
           private String password;
           private Integer age;
           private String sex;
            
            public User() 
            
            public User(String uid, String userName, String password, Integer age, String sex) 
              	this.uid = uid;
              	this.userName =userName;
                this.password = password;
                this.age = age;
                this.sex = sex;
            
            
            
           public Integer getUid() 
               return uid;
           
           public void setUid(Integer uid) 
               this.uid = uid;
           
           public String getUserName() 
               return userName;
           
           public void setUserName(String userName) 
               this.userName = userName;
           
           public String getPassword() 
               return password;
           
           public void setPassword(String password) 
               this.password = password;
           
           public Integer getAge() 
               return age;
           
           public void setAge(Integer age) 
               this.age = age;
           
           public String getSex() 
               return sex;
           
           public void setSex(String sex) 
               this.sex = sex;
           
           @Override
           public String toString() 
               return "User" +
                       "uid=" + uid +
                       ", userName='" + userName + '\\'' +
                       ", password='" + password + '\\'' +
                       ", age=" + age +
                       ", sex='" + sex + '\\'' +
                       '';
           
       
    

    UserMapper.java 映射接口

    package org.example.Mapper;
    
    import org.example.Bean.User;
    
    public interface UserMapper 
      	User getUserByUid(int uid);
    
    
  5. 获取 MyBatis 操作数据库的会话对象 sqlSession 与测试

    简单的测试当前的程序,过程中获取 sqlSession 对象

    package org.example.Main;
    
    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.example.Bean.User;
    import org.example.Mapper.UserMapper;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    public class run 
        public static void main(String[] args) throws IOException 
            //将资源开启为 inputstream 对象,加载全局配置文件
            InputStream input = Resources.getResourceAsStream("mybatis-config.xml");
    		//以全局配置文件作为参数,使用 SqlSessionFactoryBuilder 建造一个 SqlSessionFactory 对象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
    		//以 SqlSessionFactory 文件创建一个 SqlSession 对象
            //参数true表示事务将会自动提交
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
            //使用 Sqlsession 创建映射关系对象
            //getMapper() 会通过动态代理动态生成UserMapper的代理实现类
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            //调用映射关系对象的方法,进行查询
            User user = userMapper.getUserByUid(1);
            System.out.println(user);
        
    
    

    运行结果

    Useruid=1, userName='admin', password='123456', age=23, sex='男'
    

    查询成功

注意

UserMapper 是一个接口,显然是无法实例化的,此处的 userMapper 对象时一个实现了 UserMapper 的类的对象。而这个类的具体实现是由 MyBatis 完成的。

  1. 总结
  • 项目文件梳理

    上述案例中一共创建了四个文件:mybatis-config 核心配置文件、UserMapper 映射配置文件、UserMapper 映射接口、User 实体类。目前,他们之间的联系、关联如图所示。mybatis-config 文件还记录了数据库相关的信息。

  • MyBatis 实现的映射


文章在前介绍过 MyBatis —— 可以将接口和 Java 的 POJO(Plain Old Java Objects,即普通的 Java 对象)映射成数据库中的记录。本例中,MyBatis 就实现了这种映射。User 是做为 Entity 实体。UserMapper 映射接口做为 DAO。


四. mybatis-config.xml 文件配置操作

  1. <properties> 标签的使用

    <configuration> 标签下书写 <properties> 标签。指定 properties 配置。

    <configuration>
    	<properties>
        	<propertiy name = "jdbc.driver" value = "com.mysql.cj.jdbc.Driver"/>
        </properties>
    </configuration>
    

    此时 <property> 标签的属性可修改

    <environments default="development">
    	<environment id="development">
    		<dataSource>
                 ...
    			<property name = "driver" value = "$jdbc.driver">
                 ...
    		</dataSource>
          </environment>
     </environments>
    

    <propertiy> 标签相当于定义了一个键值对,name 属性 jdbc.driver 为键,value 属性为值,在该定义后,可在<configuration>标签下文中使用$jdbc.driver通过键取得值。

  2. <properties> 标签与 .properties 资源文件联用

    在项目的 resource 目录下创建 .properties 资源文件,并在全局配置文件中使用。

    jdbc.properties

    jdbc.driver = com.mysql.cj.jdbc.Driver
    jdbc.url = jdbc:mysql://127.0.0.1:3306/mybatistest?useUnicode=true&amp;characterEncoding=UTF-8&amp;userSSL=false&amp;serverTimezone=GMT%2B8
    jdbc.username = root
    jdbc.password = 123456
    

    在全局配置文件中引入 properties 文件

    <!--
    <properties>标签属性有两种
    resource:在类路径下访问资源文件
    url:在网络路径或磁盘路径下访问资源文件
    -->
    <properties resource = "jdbc.properties"></properties>
    

    在引入外部资源文件后,在全局配置中 标签下可以进行修改为

    <dataSource>
    	<proprety name = "diver" value = "$jdbc.driver" />
        <property name = "url" value = "$jdbc.driver"/>
        <property name = "username" value = "$jdbc.driver"/>
        <property name = "password" value = "$jdbc.driver"/>
    </dataSource>
    

    此方法使用 .properties 文件更有利于解耦合与后续的项目修改。.properties 资源文件的可读性与易修改性比 .xml 文件强

  3. <settings>标签的使用

    在全局配置文件中使用 <setting> 标签能够实现一些功能

    例如:

    <settings>
        <!--将数据库字段名的下划线转换为驼峰式命名法则-->
    	<setting name = "mapUnderscoreCamelCase" value = "true"/>
    </settings>
    

    因为数据库的字段命名尝用下划线分开单词,例如本例中使用 userName 为实体类,并且数据库中的字段名也为 userName,若不同,例如数据库字段为 user_name,则读取 userName 数据值将会为 null,产生错误的结果。 而该设置可以避免这一问题,将数据库的下划线字段名自动识别为驼峰式的命名,并读取数据。

    本文仅介绍这一功能,其他功能可参见 MyBatis 文档

  4. <typeAliases> 标签的使用

    为一个 Java 类设置一个更短的名称

    • 默认的类别名设置

      <typeAliases>
           <!--为类型设置别名,type值为java类型,默认别名就是类型,且不区分大小写-->
           <!--此时在 Mapper.xml 文件中可以用 user 或 User 取代全类名使用-->
      	<typeAliase type = "org.example.bean.User"/>
      </typeAliases>
      

      此时在映射文件中,如 UserMapper.xml 可以修改类名称为

      <!--resultType 属性此时使用的就是定义的别名-->
      <select id="getUserByUid" resultType="user">
      select * from User where uid = #id
      </select>
      
    • 指定别名以设置别名

      <!--与上述的默认类型别名不同,此处指定了别名为 u-->
      <typeAliases>
      	<typeAliase type = "org.example.bean.User" alias = "u"/>
      </typeAliases>
      

      此时在映射文件中,如 UserMapper.xml 可以修改类名称为

      <!--使用自定义的别名 u 代替全类名-->
      <select id="getUserByUid" resultType="u">
      select * from User where uid = #id
      </select>
      
    • <package> 标签包批量别名

      <typeAliases>
      	<package name = "org.example.bean"/>
      </typeAliases>
      

      在 org.example.bean 下的所有类全部设置为默认别名


五. 数据库的增、删、改、查、操作

对于数据库的增删改查操作需要书写映射接口,与 Mapper 映射文件。

映射接口

package org.example;

public interface UserMapper 
    //通过uid获取用户对象
    User getUserByUid(int uid);
    
    //增加用户
    void addUser(User user);
    
    //更新,修改用户
    void updateUser(User user);
    
    //删除用户
    void deleteUser(int uid);
    
    //获取用户数量
    Integer getCount();
    
    //获取全体用户对象
    List<User> getAllUser();
   
    //设置 map 的键,因为在查询时传出所有员工的信息,可以把员工信息作为值,但是必须设置键
    @MapKey("uid")
    Map<Integer, User> getAllUserMap();
    
    //以 map 集合获取一个员工信息
    @MapKey("uid")
    Map<Integer, Object> getUserMapByUid(String uid);
    

  • 返回值为 void 的方法也可以设置返回值为 Integer 或 Boolean,Integer 类型表示多少行受影响,Boolean 类型表示是否影响到行。

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">
<mapper namespace="org.example.Mapper.UserMapper">
    
    <select id="getUserByUid" resultType="User">
        select * from User where uid = #id
    </select>
    
    <!--返回值为 void 的方法无需规定 resultType 属性-->
    <insert id = "addUser">
    	insert into user value(null,#uid,#user_name,#password,#age,#sex)
    </insert>
        
    <!-- parameterTyper 属性为传入值的类型,也可以不写,Mybatis会进行类型推断-->
    <update id = "updateUser" parameterType = "User">
    	update user set user_name = #user_name, age = #age, sex = #sex where uid = #uid
    </update>
    
    <delete id = "deleteUser">
    	delete from user where uid = #uid
    </delete>
    
    <!--返回值为 Integer 来或得结果-->
    <select id = "getCount" resultType = "Integer">
    	select count(uid) from user
    </select>
    
    <!--返回值为 List<User> 但是只需要书写集合内的元素类型即可-->
    <select id = "getAllUser" resultType = "User">
        select * from user
    </select>
    
    <!--注意一下两条语句的 resultType 类型为 User 而不是 Map-->
    <select id = "getAllUserMap" resultType = "User">
    	select * from user
    </select>
    
    <select id = "getUserMapByUid" resultType = "User">
    	select * from user where uid = #uid
    </select>
</mapper>

执行测试

package org.example.Main;


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.example.Bean.User;
import org.example.Mapper.UserMapper;

import java.io.IOException;
import java.io.InputStream;

public class run 
    public static void main(String[] args) throws IOException 

        InputStream input = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
        //注意,与前一个案例不同,此处没有填入参数true。因此事务需要手动提交
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        
        //添加用户的操作
        userMapper.addUser(new User(2, "user", "1024", 20, "女"));
        
        //更新用户操作
        userMapper.updateUser(new User(1,"newName","000000",24,"男"));
        
        //根据 uid 删除用户操作
        userMapper.deleteUser(2)
        
        //计数
        System.out.println(userMapper.getCount());
            
        //返回值为 List 的全体查询
        List<User> allUserList = userMapper.getAllUser();
        for(User user : AllUserList) 
            System.out.println(user);
        
        
        //返回值为 Map 的全体查询
        Map<Integer, User> allUserMap = userMapper.getAllUserMap();
        User u1 = (User) map.get(1);
        System.out.println(u);
        
        //返回值为 Map 的按 uid 查询
        Map<Integer, User> userMap = userMapper.getUserMapByUid("1");
        User u2 = (User) map.get(1);
        System.out.println(u);
        
        //提交事务
		sqlSession.conmmit();
    

注意

本例中对于 UserMapper.xml 中的 resultType 与 Mapper 接口中方法返回值并不完全一致。