MyBatis的基本使用

概述

什么是MyBatis:MyBatis 是一个半 ORM 框架,它内部封装了 JDBC 的大部分操作,在开发的时候我们只需要关注 SQL 语法本身,而不需要花精力去处理 JDBC 的一些操作。在 MyBatis 中通过 xml 或者注解的方式,将实体类与数据库中的表进行映射,由框架执行 SQL 并将执行结果映射为实体类返回

MyBatis 的优点:

  • 相比于 JDBC,减少了大量的代码,不需要我们手动开启连接、关闭连接

  • 解耦。将 SQL 写在 xml 文件中,使 SQL 与程序代码分离,便于我们直接进行管理

  • 支持动态 SQL

  • 能够与 Spring 很好的集成,与其他数据库兼容

MyBatis 的缺点:

  • 当表中的字段多,表的关联多的时候,SQL 的编写量上升
  • SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库

#{} 与 ${} 的区别

  • #是预编译处理,$是字符串替换
  • 在处理#时,会将括号中的值用?替换,并调用 PreparedStatement 的 set 方法来赋值
  • 在处理&时,把${}​​​替换成变量的值
  • 使用#可以有效的防止 SQL 注入,提高系统安全性

快速开始

  1. 创建配置文件 mybatis-config.xml,存放于类路径下
<?xml version="1.0" encoding="UTF8" ?>
<!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">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/dbname?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=GMT"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>
    
    <mappers>
        <mapper resource="" />
    </mappers>

</configuration>
  1. 创建 SQL 映射文件 EmployeeDao.xml
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.wes.dao.EmployeeDao">

    <!--查询操作必须指定 resultType,增删改可以不写-->
    <select id="getEmployeeById" resultType="com.wes.pojo.Employee">
        select * from mybatis.employee where id = #{id}
    </select>
    
</mapper>
  1. 测试使用
public static void main(String[] args) {
    String resource = "mybatis-config.xml";
    // 读取配置文件
    InputStream inputStream = Resources.getResourceAsStream(resource);
    // SqlSessionFactoryBuilder -> SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
    // 获取和数据库的一次会话
    // openSession(true):自动提交
    SqlSession sqlSession = sqlSessionFactory.openSession();
    
    // 通过会话,操作数据库
  	// 这里获取到 dao 接口的实现,获取到的是接口的代理对象,这个对象是mybatis自动创建的
    EmployeeDao mapper = sqlSession.getMapper(EmployeeDao.class);

    Employee employee = mapper.getEmployeeById(1);
}

每一个 MyBatis 应用都是以一个 SQLSessionFactory 的实例为核心的,该实例通过 SqlSessionFactoryBuilder 获得

而 SqlSessionFactoryBuilder 可以从 xml 或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例

从 sqlSessionFactory 获取的 SqlSession 完全包含了面向数据库执行 SQL 命令所需要的所有方法,可以通过 SqlSession 实例来直接执行已映射的 SQL 语句

全局配置文件

全局配置文件 mybatis-config.xml

properties

作用:引入外部配置文件

<properties resource="" /> 从类路径下开始引用
<properties url="" /> 引用磁盘路径或网路路径的资源

演示:

username=root
password=1234
url=jdbc:mysql://localhost:3306/mybatis
driverClassName= com.mysql.cj.jdbc.Driver
<?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>

    <properties resource="db.properties">
        <!--可以添加属性,优先使用外部文件-->
        <property name="username" value="" />
    </properties>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driverClassName}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="EmployeeDao.xml" />
    </mappers>

</configuration>

settings

改变 MyBatis 的运行时行为

所有设置:https://mybatis.org/mybatis-3/zh/configuration.html#settings

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

    <properties resource="db.properties"/>

    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--开启驼峰命名自动映射: A_COLUMN -> aColumn(pojo)-->
    </settings>
    
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driverClassName}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="EmployeeDao.xml" />
    </mappers>

</configuration>

typeAliases

作用:为常用的 Java 类名起别名(不推荐使用)

<select id="getEmployeeById" resultType="employee">
    select * from mybatis.employee where id = #{id}
</select>
<typeAliases>
    <!--默认为类名,不区分大小写;也可以使用alias自定义-->
    <typeAlias type="com.wes.pojo.Employee" alias="employee" />

    <!--批量起别名,默认别名就是类名,不区分大小写-->
    <package name="com.wes.pojo" />
</typeAliases>

当使用了 package 还想给某个特定类起别名,则在类的上面添加注解 @Alias(“ ”)

environments

<environments default="development">
    <!--environment配置一个具体的环境,都需要一个事务管理器和一个数据源-->
    <environment id="development">
        <!--事务管理器有两种:JDBC、MANAGED-->
        <transactionManager type="JDBC"/>
        <!--POOLED:使用连接池技术-->
        <dataSource type="POOLED">
            <property name="driver" value="${driverClassName}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
</environments>

mappers

作用:注册绑定 SQL 映射文件

<mappers>
    <mapper resource="EmployeeDao.xml" />	在类路径找映射文件
    <mapper class="com.wes.dao.EmployeeDao"/>	全类名
    <mapper url="" />	从磁盘或网络路径中引用
    
    <package name="com.wes.pojo" />	批量注册
</mappers>

映射时,映射文件名与接口名要一致,且在同个包目录下;或者在 resources 中创建同结构包

SQL 映射文件

MyBatis 中常用的标签有:insert、delete、update、select;resultMap、sql(抽取 SQL)、cache、cache-ref

CRUD

resultType:select 必须指定,其他可不必要

parameterType:参数类型。可选,默认不写

<!--查询操作必须指定 resultType返回类型,增删改可以不写-->
<select id="..." resultType="com.wes.pojo.Employee">
    select * from emp
</select>

<insert id="..">
    insert into emp value (#{id}, #{name}, #{gender}, #{email})
</insert>

<delete id="..">
    delete from emp where id = #{id}
</delete>

<update id="..">
    update emp set name = #{name} where id = #{id}
</update>

对于增删改操作,需要开启事务

SqlSession sqlSession = sqlSessionFactory.openSession(true);

获取主键

useGeneratedKeys:取出数据库生成的主键,仅对 insert、update 有效

keyProperty:实体类中的主键字段,唯一标记一个属性,仅对 insert、update 有效

<insert id=".." useGeneratedKeys="true" keyProperty="id">
    insert into emp value (null, #{name}, #{gender})
</insert>
@Test
public void test() throws IOException {
    Emp emp = new Emp(null, "wes", 0);
    int id = emp.getId();
    System.out.println("插入的 id :" + employee.getId());
}

模糊查询

  • 在 Java 代码层面,使用通配符传递参数
  • 在映射文件中,使用通配符
List<User> userList = mapper.getUserLike("%月%");
<select id=".." resultType="Product">
    select * from user where name like concat('%', '#{name}', '%')
</select>

分页

select * from user limit startIndex, pageSize;
select * from user limit 0, 2;	-- 每页显示2个
select * from user limit 3;	-- 显示前三个数据

创建一个 pageBean 实体类,在类中维护 startIndex,pageSize 两个属性,用来表示开始位置和每页记录数

<select id=".." resultMap="User">
    select * from user limit #{startIndex}, #{pageSize}
</select>
HashMap<String, Integer> map = new HashMap<>();
map.put("startIndex", 0);
map.put("pageSize", 2);
List<User> list = mapper.getUserByLimit(map);

其他实现:RowBounds 实现分页,通过 Java 代码层面实现分页。通过第三方插件实现(例如:PageHelper)

参数传入

从接口中传入不同个数的参数进入映射中

  • 单个:直接传入
  • 多个:使用 @Param(" ") 定义参数名

如何不使用该注解显示定义参数名,会报错 Parameter ‘id’ not found. Available parameters are [arg1, arg0, param1, param2]

<select id=".." resultType="">
    select * from emp where id = #{arg0} and name = #{arg1}
    select * from employee where id = #{param1} and name = #{param2}
</select>
  • 当传入参数既有基本类型,又有对象时:
int addEmployee(@Param("id") Integer id, Employee employee);

参数获取

在映射文件中,使用 #{} 获取参数

  • 当传入的参数只有一个:括号中的值任意(#{123})
  • 当传入的参数不止一个:
  • 当传入的是实体:#{对象名.属性}
  • 当传入的是 Map:#{key}

参数返回

除了返回 int、对象之外,SQL 中还可以返回 Map 给 DAO 层

Map<String, Object> method();
<select id="method" resultType="map">
    select * from emp
</select>

获取到的 Map 以特定字段名为 key,指定值为 value

@MapKey("id")	// 以 id 为 key
Map<Integer, Employee> method();
<select id="method" resultType="Employee">
    select * from emp
</select>
Map<String, Emp> map = mapper.method();
Emp emp = map.get(1);

ResultMap

ResultMap:结果集映射

使用 ResultMap 可以自定义封装规则,解决属性名、数据库与实体类字段名不一致问题

字段名不一致问题:起别名 / 结果集映射

<select id="getUserById" resultType="User" parameterType="int">
    select id, name, psw as password from user where id = #{id};
</select>
<select id="getUserById" resultMap="UserMap" parameterType="int">
    select * from user where id = #{id};
</select>

<resultMap id="UserMap" type="user">
    <id property="id" column="id" />
    <result column="psw" property="password" />
</resultMap>
  • column:数据库字段
  • property:实体类字段

association

在 ResultMap 中,可以使用 association 来关联其他对象

子查询方式:

<select id="query1" resultMap="Student_Teacher">
	select * from student
</select>

<select id="query2" resultType="Teacher">
    select * from teacher where id = #{id}
</select>

<resultMap id="Student_Teacher" type="Student">
    <!--property:实体类字段;column:数据库字段-->
    <result property="id" column="id" />
    <result property="name" column="name" />
    <!--javaType:指定这个属性的类型 column:传入 tid 给 子查询-->
    <association property="teacher" column="tid" javaType="Teacher" select="query2"/>
</resultMap>

连表查询:

<select id="query" resultMap="Student_Teacher">
    select s.id sid, s.name sname, t.id tid t.name tname
    from student s join teacher t on s.tid = t.id
</select>

<resultMap id="Student_Teacher" type="Student">
    <result property="id" column="sid" />
    <result property="name" column="sname" />
    <association property="teacher" javaType="Teacher">
        <result property="id" column="tid" />
        <result property="name" column="tname" />
    </association>
</resultMap>
public class Student {
    private int id;
    private String name;
    private Teacher teacher;
}

collection

使用 collection 做集合

public class Teacher {
    private int tid;
    private String tname;
    private List<Student> students;
}
public class Student {
    private int sid;
    private String sname;
    private int tid;
}

子查询:

<select id="query1" resultMap="Teacher_Student">
    select * from teacher where tid = #{tid}
</select>

<select id="query2" resultType="Student">
    select * from student where tid = #{tid}
</select>

<resultMap id="Teacher_Student" type="Teacher">
    <!--javaType:集合类型, ofType:集合属性类型-->
    <collection property="students" column="tid" javaType="ArrayList" ofType="Student" select="query2" />
</resultMap>

联表查询:

<select id="queryTeacher" resultMap="Teacher_Student">
    select t.tid, t.tname, s.sid, s.sname
    from student s join teacher t on t.tid = s.tid
</select>

<resultMap id="Teacher_Student" type="Teacher">
    <result property="tid" column="tid" />	
    <result property="tname" column="tname" />
    <collection property="students" ofType="Student" >
        <result property="sid" column="sid" />
        <result property="sname" column="sname" />
        <result property="tid" column="tid" />
    </collection>
</resultMap>

日志工厂

作用:使控制台输出日志信息

常用:LOG4JSTDOUT_LOGGING

<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

建立 log4j.properties

#将等级为DEBUG的日志信息输出到console和file这两个目的地
log4j.rootLogger=debug,console,file

#console
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold = DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = [%c]-%m%n

log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File = ./log/yue.log
log4j.appender.file.MaxFileSize = 10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = [%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis = DEBUG
log4j.logger.java.sql = DEBUG
log4j.logger.java.sql.Statement = DEBUG
log4j.logger.java.sql.ResultSet = DEBUG
log4j.logger.java.sql.PreparedStatement = DEBUG

动态SQL

IF

<select id=".." resultType="blog">
    select * from blog
    <where>
        <if test="title != null">
            and title = #{title}
        </if>
    </where>
</select>

choose

choose:多选一,类似于 switch

<select id="..." resultType="Blog">
    SELECT * FROM BLOG WHERE
    <where>
        <choose>
            <when test="title != null and !title.equals(&quot;&quot;)">
                title = #{title}
            </when>
            <otherwise>
                AND views = #{views}
            </otherwise>
        </choose>
    </where>
</select>

set

<update id="updateBlog" parameterType="map">
    update blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">            
            author = #{author},
        </if>
    </set>
    <where>
        <if test="id != null">
            id = #{id}
        </if>
    </where>
</update>

trim

where、set 的自定义:trim

<select id="..." resultType="Blog">
    <!--prefix:前缀, siffix:后缀-->
    <trim prefix="WHERE" prefixOverrides="AND |OR" siffix="and">    
        <if test="state != null">
            state = #{state} and
        </if>
        <if test="title != null">
            AND title like #{title} and
        </if>
    </trim>
</select>
<trim prefix="SET" suffixOverrides=",">    ...</trim>

foreach

作用:对一个集合进行遍历,通常是在构建 IN 条件语句的时候

select * from blog where 1 = 1 and (id = 1 or id = 2 or id = 3)
<select id=".." resultType="blog">
    select * from blog
    <where>
        <if test="ids.size > 0">
            <!--separator:元素之间的分隔符-->
            <foreach collection="ids" item="id" open="and ("  close=")" separator="or">
                id = #{id}
            </foreach>
        </if>
    </where>
</select>
HashMap map = new HashMap();
ArrayList<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
ids.add(3);
map.put("ids", ids);
List<Blog> blogs = mapper.queryBlogForeach(map);

SQL

作用:把一些功能的部分抽取出来,方便复用

<sql id="if-title-author">    
    <if test="title != null">
        title = #{title}
    </if>
    <if test="author">
        and author = #{author}
    </if>
</sql>
<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <include refid="if-title-author"></include>
    </where>
</select>

缓存

MyBatis 的两种缓存:一级缓存、二级缓存

默认开启一级缓存(本地缓存),一级缓存是线程级别的缓存,SqlSession 级别的缓存,只在一个 sqlSession 中有效

清除缓存:sqlSession.clearCache()

二级缓存是全局范围的缓存,在 SqlSession 关闭或提交后才会生效

<settin name="cacheEnabled" value="true" />

在需要使用二级缓存的映射文件出使用 cache 配置缓存 <cache />

原理:一级与二级不会存在同一个数据,先看二级,再看一次

注解

  1. 使用注解之前需要先打开自动提交事务
public static SqlSession getSqlSession() {
    return sqlSessionFactory.openSession(true);
}
  1. 在接口中使用注解
@Select("select * from user")
List<User> getUsers();

@Insert("insert into user(id, name, psw) values (null, #{name}, #{password})")
int addUser(User user);

@Update("update user set name=#{name}, psw=#{password} where id = #{id}")
int updateUser(User user);

@Delete("delete from user where id = #{id}")
int deleteUser(@Param("id") int id);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值