Mybatis

149 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

MyBatis快速入门

  • MyBatis是一款优秀的持久层框架,用于简化JDBC开发
  • MyBatis 本是 Apache的一个开源项目iBatis, 2010年这个项目由apache softwarefoundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github
  • 官网: mybatis.org/mybatis-3/z…

持久层

  • 负责将数据到保存到数据库的那一层代码
  • JavaEE三层架构:表现层,业务层,持久层

框架

  • 框架就是一个半成品软件,是一套可重用的,通用的,软件基础代码模型
  • 在框架的基础之上构建软件编写更加高效,规范,通用,可扩展

JDBC的缺点:

1.硬编码:注册驱动,获取连接。 SQL语句

2.操作繁琐:手动设置参数。 手动封装结果集

image.png

因此,MyBatis就是用来解决JDBC的缺点的

image.png

image.png 具体流程:

image.png 1.在数据库中进行查询

create database mybatis;
use mybatis;

drop table if exists tb_user;

create table tb_user(
	id int primary key auto_increment,
	username varchar(20),
	password varchar(20),
	gender char(1),
	addr varchar(30)
);



INSERT INTO tb_user VALUES (1, 'zhangsan', '123', '男', '北京');
INSERT INTO tb_user VALUES (2, '李四', '234', '女', '天津');
INSERT INTO tb_user VALUES (3, '王五', '11', '男', '西安');

2.在pom.xml中加入如下代码

<dependency>
    <!--mybatis 依赖 -->
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.5</version>
</dependency>
<!-- mysql 驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.32</version>
</dependency>

3.创建一个mybatis-config.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="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--数据库连接信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environments>
    <mappers>
        <!--加载sql映射文件-->
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>

4.创建一个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:名称空间
    -->
<mapper namespace="test">
    <select id="selectAll" resultType="xiaokasidi.User">
        select * from tb_user;
    </select>
</mapper>

5.编码

//1.加载Mybatis的核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

//2.获取对应SqlSession对象,执行SQL语句
SqlSession sqlSession = sqlSessionFactory.openSession();

//3.执行sql
List<User> users = sqlSession.selectList("test.selectAll");
System.out.println(users);

//4.关闭资源
sqlSession.close();

mysql8.0后要记得在下图第二行中加入serverTimezone=GMT

image.png

Mapper代理开发

image.png 1.定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下。

注意:在resource文件夹下,创建新文件夹并且命名时,要用/不能用.

2.设置SQL映射文件的namespace属性为Mapper接口全限定名

在UserMapper.xml文件下

image.png

3.在Mapper接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致

4.编码

1.通过SqlSession的getMapper方法获取Mapper接口的代理对象

2.调用对应方法完成sql的执行

UserMapper usermapper = sqlSession.getMapper(UserMapper.class);
List<User> users = usermapper.selectAll();
System.out.println(users);

配置文件完成增删改查

环境准备:

image.png

查询:1.查询所有数据

image.png 在进行查询前,最好都进行如上的分析

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

//2.获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();

//3.获取Mapper接口的代理对象
BrandMapper branmapper = sqlSession.getMapper(BrandMapper.class);

//4.执行方法
List<Brand> brands = branmapper.selectAll();
System.out.println(brands);

//5.释放资源
sqlSession.close();

bug1:运行时,出了个BindingException的bug

原因:没在config.xml文件中加入

bug2:报mybatis.tb_brand错误。

原因:数据库表名错了

数据库表的字段名称和实体类的属性名称不一样,则不能自动封装数据

1.起别名:对不一样的的列名起别名,让别名和实体类的属性名一样 缺点:每次查询都要定义一次别名

<select id="selectAll" resultType="xiaokasidi.Brand">
    select  id,brand_name as brandName,company_name as companyName,ordered,description,status
    from tb_brand;
</select>

2.sql片段,缺点:不灵活

<sql id="brand_column">
    id,brand_name as brandName,company_name as companyName,ordered,description,status
</sql>
<select id="selectAll" resultType="xiaokasidi.Brand">
    select  <include refid="brand_column"></include>
    from tb_brand;
</select>

3.resultMap

1.定义resultMap标签

2.在select使用resultMap属性替换resultType属性

    id:唯一标识
    type:映射的类型,支持别名
    -->
<resultMap id="brandResult" type="xiaokasidi.Brand">
    <!--
    id:完成主键字段的映射
        column:表的列名
        property:实体类的属性名
    result:完成一般字段的映射
        column:表的列名
        property:实体类的属性名
    -->
    <result column="brand_name" property="brandName"></result>
    <result column="company_name" property="companyName"></result>
</resultMap>

    <select id="selectAll" resultMap="brandResult">
        select  *
        from tb_brand;
    </select>

2.查看详情

image.png

参数占位符:
        1.#{}:会将其替换为?,为了防止sql注入
        2.${}:拼sql。会存在sql注入问题
        3.使用时机:
            *参数传递的时候:#{}
            *表名或者列名不固定的情况下:${}会存在sql注入问题

        参数类型:parameterType:可以省略
        特殊字符处理:
            1.转义字符:例:< -> &lt
            2.CDATA区:<![CDATA[
                <
            ]]>

    -->
<select id="selectById" parameterType="int" resultMap="brandResult">
    select  *
    from tb_brand where id = #{id};
</select>

3.条件查询

多条件查询

有三种不同的参数

public void testSelectByCondition() throws IOException {
        //接收参数
        int status = 1;
        String companyName = "华为";
        String brandName = "华为";

        //处理参数  1.散装参数  2.对象参数
        companyName = "%" + companyName + "%";
        brandName = "%" + brandName + "%";

        //封装对象  2.对象参数
//        Brand brand = new Brand();
//        brand.setStatus(status);
//        brand.setCompanyName(companyName);
//        brand.setBrandName(brandName);
        // 封装对象  3.map集合参数
        Map map = new HashMap();
        map.put("status" , status);
        map.put("companyName",companyName);
        map.put("brandName",brandName);

        //1.获取SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //2.获取SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //3.获取Mapper接口的代理对象
        BrandMapper branmapper = sqlSession.getMapper(BrandMapper.class);

        //4.执行方法
        //1.散装参数List<Brand> brands = branmapper.selectByCondition(status,companyName,brandName);
        List<Brand> brands = branmapper.selectByCondition(map);
        System.out.println(brands);
        //5.关闭资源
        sqlSession.close();
    }

image.png 但上述查询有些许不灵活,若用户只想根据一个条件进行查询,那么将无法达到目的。

因此,需要做出相应改变

多条件动态查询

image.png

<!--
if:条件判断
    test:逻辑表达式
存在问题:如果第一个条件不存在,and会到sql语句中
    解决:恒等式:1.加上1=1
                2.用</where>标签
-->
<select id="selectByCondition" resultMap="brandResult">
    select *
    from tb_brand
    <where>
          <if test="status != null">
              and status = #{status}
          </if>
        <if test="companyName != null and companyName != ''">
            and company_name like#{companyName}
        </if>
        <if test="brandName != null and brandName != '' ">
    and brand_name like#{brandName}
</if>
    </where>
        </select>

这时就可以只根据其中一个值就能进行查询

单条件动态条件查询

image.png

<!--
    单条件动态查询语句
-->
<select id="selectByConditionSingle" resultMap="brandResult">
    select *
    from tb_brand
    where
    <choose><!--相当于switch-->
        <when test="status != null"><!--相当于case-->
          status = #{status}
        </when>
        <when test="companyName != null and companyName != ''">
            company_name like#{companyName}
        </when>
        <when test="brandName != null and brandName != '' ">
            brand_name like#{brandName}
        </when>
        <otherwise><!--相当于default-->
            1 = 1;
        </otherwise>
    </choose>
</select>

添加

image.png

image.png

<insert id="add">



    insert into tb_brand(brand_name,company_name,ordered,description,status)



    values (#{brandName},#{companyName},#{ordered},#{description},#{status});



</insert>

image.png

<insert id="add" useGeneratedKeys="true" keyProperty="id">
    insert into tb_brand(brand_name,company_name,ordered,description,status)
    values (#{brandName},#{companyName},#{ordered},#{description},#{status});
</insert>

修改

修改全部字段

image.png 但上述操作在只修改其中一些字段时会导致其他字段被迫设置为null,这是我们不希望看到的,因此要设置为动态的

修改动态字段

image.png

<update id="update">
    update tb_brand
    <set>
        <if test="brandName != null and brandName != ''">
            brand_name = #{brandName},
        </if>
        <if test="companyName != null and companyName != ''">
            company_name = #{companyName},
        </if>
        <if test="ordered != null">
            ordered = #{ordered},
        </if>
        <if test="description != null and description != ''">
            description = #{description},
        </if>
        <if test="status != null">
            status = #{status}
        </if>
    </set>
    where id = #{id};
</update>

删除

删除一个

image.png

delete
        from tb_brand where id = #{id};
</delete>

批量删除

image.png

<delete id="delById">
        delete
            from tb_brand where id = #{id};
    </delete>
    <delete id="delByIds">
        delete from tb_brand where id
        in
        <foreach collection="array" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
        ;
    </delete>

Mybatis参数传递

image.png

image.png

Mybatis是如何通过@Param来

多个参数:封装为map集合

map.put("arg0",参数值1)

map.put("parm1",参数值1)

map.put("parm2",参数值2)

map.put("arg1",参数值2)

源码分析:ParamNmaeResolver

很好懂,可以自己点进去看

image.png

注解开发

image.png 直接在java文件中

@Select("select * from `<xiaokasidi>`.tb_brand where id = #{id}")
Brand selectById(int id);