mybatis框架 配置及应用(xml文件形式的增删改、接口标签形式的增删改、 #{ } 与 ${ } 的区别、日志工具监控mybatis生成的sql语句)

mybatis的概述

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射,它避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集,它可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

框架分类

1) 持久层框架 (与数据库打交道)
   ORM 框架   object(java对象)  relationship (关系型数据库 mysql) mapping (映射-java对象和数据库之间)

   持久层框架有:mybatis(大型项目-直接管理sql),hibernate(小型项目-不需要直接写sql语句),jpa,jooq ...

2) mvc 框架(跟 web 应用程序打交道)
   
   mvc 框架  Model(模型-数据) View(视图-数据展现方式) Controller(控制器)

   把模型数据准备好,  把数据放入作用域, 最后通过jsp视图展现数据

   mvc 框架:springmvc, struts2

3) spring 框架 (容器技术)
   把各种框架进行集成,让他们协同工作

配置

  • 添加依赖( pom.xml 中添加 mybatis 依赖)

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

  • 添加配置文件 mybatis-config.xml(resources/mybatis-config.xml )

        用这个配置文件来告诉 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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <!-- 配置了 mybatis 连接数据库的信息 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/数据库名?useSSL=false"/>
                <property name="username" value="数据库用户名"/>
                <property name="password" value="数据库密码"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 指定映射文件的位置,以下两个xml文件用第一个,反之则第二个 如:mapper/StudentMapper.xml-->
        <mapper resource="映射文件的位置"/>
        <mapper class="接口 mapper的 包名.类名"/>
    </mappers>
</configuration>
  • 提供一个映射文件

        用来管理 sql 语句,描述 sql 语句与数据库表之间的映射关系 。

<!-- namespace(命名空间) 用来防止 sql 的命名冲突的 -->
<mapper namespace="命名空间">
    <insert id="sql的名字" parameterType="参数类型" useGeneratedKeys="true" keyProperty="主键对应的属性">
    <update id="sql的名字" parameterType="参数类型">
    <delete id="sql的名字" parameterType="参数类型">
    <select id="sql的名字" parameterType="参数类型" resultType="结果类型,如果是集合是集合中元素的类型">

    <if test="条件"> sql 片段 </if>
    <set></set>
</mapper>

参数类型: 
    int, string, #{名字任意}
    map #{key}
    自定义javabean #{属性名}

如:

扫描二维码关注公众号,回复: 5334941 查看本文章
<!-- namespace(命名空间) 也就是xml(写着SQL语句的xml)的位置 用来防止 sql 的命名冲突的 -->
<mapper namespace="mapper.StudentMapper">
新增

<!-- #{sname} 用来获取 Student 参数对象中的 sname属性-->
<!-- useGeneratedKeys="true" 是告诉 mybatis 要使用由数据库产生的主键值 -->
<!-- keyProperty="主键对应的属性名" -->
<insert id="abc" parameterType="domain.Student"
        useGeneratedKeys="true" keyProperty="sid">
    insert into student(sid, sname, birthday, sex)
        values ( null, #{name}, #{birthday}, #{sex})
</insert>

删除

<delete id="delete" parameterType="int">
    delete from student where sid = #{sid}
</delete>

查询所有

<select id="findAll" resultType="domain.Student">
    select sid,sname name,birthday,sex from student
</select>

根据id查询

<select id="findById" resultType="domain.Student" parameterType="int">
    select sid,sname name,birthday,sex from student where sid = #{sid}
</select>

更新

<update id="update" parameterType="domain.Student">
    update student set sname=#{name}, birthday=#{birthday}, sex=#{sex} where sid=#{sid}
</update>

用  set 标签可以去除多余的逗号(动态更新列)

<update id="update" parameterType="domain.Student">
    update student
    <set>
        <if test="name != null">
            sname=#{name},
        </if>
        <if test="birthday != null">
            birthday=#{birthday},
        </if>
        <if test="sex != null">
            sex=#{sex},
        </if>
    </set>
    where sid=#{sid}
</update>
</mapper>

通过日志工具监控mybatis生成的sql语句

logback  利用命名空间可以控制哪些sql会作为日志被输出。

  • 添加依赖

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

  • 添加一个 logback.xml 到 resources文件夹
<?xml version="1.0" encoding="UTF-8"?>
<configuration
        xmlns="http://ch.qos.logback/xml/ns/logback"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback logback.xsd">
    <!-- 输出控制,格式控制-->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%date{yyyy-MM-dd HH:mm:ss} [%-5level] %logger{32} - %m%n </pattern>
        </encoder>
    </appender>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 日志文件名称 -->
        <file>logFile.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天产生一个新的日志文件 -->
            <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 保留 15 天的日志 ,根据需求更改-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%date{yyyy-MM-dd HH:mm:ss} [%-5level] %logger{17} - %m%n </pattern>
        </encoder>
    </appender>

<!-- 用来控制查看那个类的日志内容(对mybatis name 代表命名空间) -->
<logger name="命名空间,也就是xml(写着SQL语句的xml)的位置" level="DEBUG" additivity="false">
    <appender-ref ref="STDOUT"/>
    <appender-ref ref="FILE"/>
</logger>

mybatis api

  • MyBatis 中重要的接口和类

SqlSessionFactory (接口) - 创建 sqlsession 的
SqlSessionFactoryBuilder (类) - 创建工厂对象
SqlSession (接口) - 执行增删改查,管理事务

代码实现如下:

InputStream in = Resources.getResourceAsStream("配置文件的路径");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = factory.openSession();

  • sqlSession 中重要的方法

sqlSession
    .insert("命名空间+sql的名字", sql需要的参数对象)
    .update("命名空间+sql的名字", sql需要的参数对象)
    .delete("命名空间+sql的名字", sql需要的参数对象)
    .selectOne("命名空间+sql的名字", sql需要的参数对象)  结果只有一个时 
    .selectList("命名空间+sql的名字", sql需要的参数对象) 结果是list集合

对增删改
sqlSession.commit();

关闭
sqlSession.close();

动态 sql 生成

  • foreach 循环

普通SQL(写死)delete  from 表 where id in(1008,1011,1012);

动态
<!-- list (1008,1011,1012, 1013) -->
<delete id="" parameterType="list">
delete  from 表 where id in  
</delete>

<!-- open表示集合里开始遍历的标志,close表示结束标志,separater表示分隔符 -->

<foreach collection="集合" item="临时变量名" open="(" close=")" separater=",">

  • if where 生成动态sql

<where> </where>能够生成 where 关键字,并去除多余的 and
<if test="条件"> sql 片段 </if>

如:

<select id="findAll" resultType="domain.Student">

select * from a 

<where>

<if test="id=5"> id=#{id} and</if>

<if test="name=张三"> name=#{name} and</if>

<if test="sex=男"> sex=#{sex} and</if>

</where>

</select>

如果三个火两两成立的话,会多出一个and,这是where标签会自动排除多余的and

  • 对于大于小于条件的处理

方法1: 对每个特殊字符转转义,例如 <  &lt;    > &gt;
方法2: <![CDATA[  内容  ]]>

 #{ } 与 ${ } 的区别

  1. #{ } 底层是替换为 ?, 只能占位值,相对安全,只能代替值,不能运算
  2. ${ } 底层是直接拼接字符串, 有注入攻击问题,可以做简单运算,可以代替sql中的任意部分
  3. 尽量使用 #{ } , 只有在某些功能不能用 #{ } 实现时(例如排序)采用  ${ }

Mapper 接口(接口方式的映射)

  • 接口方式的映射

@Insert ("sql")
@Option("sql")
@Update("sql")
@Delete("sql")
@Select("sql")

  • 原理

// 这个类是使用了 jdk的动态代理技术 在代码运行期间生成的类 
public class $Proxy10 implements StudentMapper{
    private SqlSession sqlSession;
    public $Proxy10(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }
    // 其中 sql语句从@Insert注解获得, 参数对象就是student
    public void insert(Student student) {
        sqlSession.insert(sql, 参数对象)
    }

// 其中 sql语句从@Select注解获得
    public List<Student> findAll() {
        return sqlSession.selectList(sql);
    }
}

 Mapper接口不足

1. 接口方法不能直接应用多个方法参数

解决方法:
1) 用map传递多个参数, 每个参数对应map中的一个键值对
2) 用@Param注解

2. Mapper 接口中不能有方法的重载
定义方法是不能方法名冲突
异常信息:
Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for 接口名.方法名(重复的)

3. 使用Mapper接口方式实现动态sql比较复杂
方法1: 结合Mapper接口和xml文件

复杂sql映射

除了使用 xml mapper 以外,还可以利用 @DeleteProvider @InsertProvider @SelectProvider @UpdateProvider生成复杂sql

@DeleteProvider(
            //这个文件里有该方法,方法的实现在这个文件中
            type = java文件.class,
            method = 方法名
    )
    void deleteByIds(List<Integer> ids);

//如下:由 @DeleteProvider 注解找到一条sql语句,供 deleteByIds 方法使用
@DeleteProvider(
            type = StudentSqlProvider.class,
            method = "getDeleteByIdsSql"
    )
    void deleteByIds(List<Integer> ids);

 高级映射

列别名与属性不一致的情况下:

  • 方法一:给列起一个列别名 
  • 方法二:使用 `<resultMap>`

缓存

  • 一级缓存

sqlsession 自带缓存功能,缓存没有才查库,缓存中有,直接返回缓存的结果,称为一级缓存,在sqlsession创建时建立,sqlsession.close 时清空,每个sqlsession有自己独立的缓存

  • 二级缓存

需要额外设置,但可以允许多个 sqlsession 共享缓存数据,二级缓存中的对象需要实现序列化接口

      配置

xml mapper 二级缓存的配置: 在 xml 映射文件中添加`<cache/>`标签即可

接口 mapper 二级缓存的配置: 在接口上添加 @CacheNamespace 注解

好处: 可以较大提升查询效率, 但是增删改频繁的情况下,不适合开启二级缓存。

代码演示

1、xml文件形式的增删改

目录结构

存储学生信息的代码

import java.util.Date;

public class Student {
    private int sid;

    private String name;

    private Date birthday;

    private String sex;

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", sname='" + name + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                '}';
    }
}

StudentMapper.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(命名空间) 用来防止 sql 的命名冲突的 也就是防止相同文件名,分不清的情况,因此标上包名来分辨-->
<mapper namespace="mapper.StudentMapper">
    <!-- #{sname} 用来获取 Student 参数对象中的 sname属性-->
    <!-- useGeneratedKeys="true" 是告诉 mybatis 要使用由数据库产生的主键值 -->
    <!-- keyProperty="主键对应的属性名" -->
    <!--parameterType表示#{}中数据的类型,这里插入代码花括号里面的数据是Student类型,因此写上domain.Student(写上包名)-->
    <insert id="abc" parameterType="domain.Student"
            useGeneratedKeys="true" keyProperty="sid">
        insert into student(sid, sname, birthday, sex)
          values ( null, #{name}, #{birthday}, #{sex})
    </insert>
    <!--#{sid}是学号,学号是int类型,因此parameterType="int"-->
    <delete id="delete" parameterType="int">
        delete from student where sid = #{sid}
    </delete>

    <select id="findAll" resultType="domain.Student">
      select sid,sname name,birthday,sex from student
    </select>

    <select id="findById" resultType="domain.Student" parameterType="int">
        select sid,sname name,birthday,sex from student where sid = #{sid}
    </select>

    <!-- 更新所有列
    <update id="update" parameterType="domain.Student">
        update student set sname=#{name}, birthday=#{birthday}, sex=#{sex} where sid=#{sid}
    </update>
    -->

    <!-- 动态更新列
    Student stu = new Student();
    stu.setSid(1001);
    stu.setSex("男");

    update student set sex=#{sex} where sid=#{sid}
    用  set 标签可以去除多余的逗号
    -->
    <update id="update" parameterType="domain.Student">
        update student
        <set>
            <if test="name != null">
                sname=#{name},
            </if>
            <if test="birthday != null">
                birthday=#{birthday},
            </if>
            <if test="sex != null">
                sex=#{sex},
            </if>
        </set>
        where sid=#{sid}
    </update>

    <!-- m , n
    java.util.Map -> map
    java.util.List -> list
    java.lang.String -> string
    map.put("m", 0);
    map.put("n", 5);
    resultType="domain.Student"表示结果的类型,因为要查的是学生的信息,因此类型是Student类型
    -->
    <select id="findByPage" parameterType="map" resultType="domain.Student">
        select sid,sname name,birthday,sex from student limit #{m}, #{n}
    </select>
</mapper>

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"/>
            <!-- 配置了 mybatis 连接数据库的信息 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/school?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 指定映射文件的位置 ,也就是有增删改内容的xml文件的位置-->
        <mapper resource="mapper/StudentMapper.xml"/>
    </mappers>
</configuration>

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration
        xmlns="http://ch.qos.logback/xml/ns/logback"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback logback.xsd">
    <!-- 输出控制,格式控制-->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%date{yyyy-MM-dd HH:mm:ss} [%-5level] %logger{32} - %m%n </pattern>
        </encoder>
    </appender>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 日志文件名称 -->
        <file>logFile.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 每天产生一个新的日志文件 -->
            <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!-- 保留 15 天的日志 -->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%date{yyyy-MM-dd HH:mm:ss} [%-5level] %logger{17} - %m%n </pattern>
        </encoder>
    </appender>

    <!-- 用来控制查看那个类的日志内容(对mybatis name 代表命名空间) 这和前面有增删改内容的xml文件的命名空间一样的意思-->
    <logger name="mapper.StudentMapper" level="DEBUG" additivity="false">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </logger>

    <root level="ERROR">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

测试代码 

import domain.Student;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TestStudentMapper {
    static SqlSessionFactory factory;
    static {
        // 读取配置文件
        try(InputStream in = Resources.getResourceAsStream("mybatis-config.xml")){
            // 创建 sqlSession 工厂类
            factory = new SqlSessionFactoryBuilder().build(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Test
    public void testInsert() throws IOException {

        // 创建 sqlSession, 这里的 session 更类似于 jdbc 中的 Connection
        SqlSession sqlSession = factory.openSession();


        // 执行新增
        Student stu = new Student();
        stu.setName("李五");
        stu.setBirthday(new Date());
        stu.setSex("男");
        // 参数1 : namespace+sql_id ,  参数2: 传递sql语句需要的java对象
        sqlSession.insert("mapper.StudentMapper.abc", stu );

        // 执行 增删改, 没有启用自动提交事务
        sqlSession.commit();

        System.out.println("sid的值:" + stu.getSid());

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

    @Test
    public void testDelete() {
        SqlSession sqlSession = factory.openSession();
        sqlSession.delete("mapper.StudentMapper.delete",
                1009);
        sqlSession.commit();
        sqlSession.close();
    }

    @Test
    public void testFindAll() {
        // selectList 用在查询多个结果
        SqlSession sqlSession = factory.openSession();
        List<Student> list = sqlSession.selectList("mapper.StudentMapper.findAll");
        sqlSession.close();
        for (Student student : list) {
            System.out.println(student);
        }
    }

    @Test
    public void testFindById() {
        // selectOne 只能用在查询结果唯一的情况下,如果结果有多个,selectOne会报异常
        SqlSession sqlSession = factory.openSession();
        Student stu = sqlSession.selectOne("mapper.StudentMapper.findById",
                1001);
        sqlSession.close();
        System.out.println(stu);
    }

    @Test
    public void testUpdate() {
        SqlSession sqlSession = factory.openSession();
        Student stu = new Student();
        stu.setSex("男");
        stu.setSid(1001);
        sqlSession.update("mapper.StudentMapper.update", stu);

        sqlSession.commit();
        sqlSession.close();
    }

    @Test
    public void testFindByPage() {
        SqlSession sqlSession = factory.openSession();
        Map<String, Integer> map = new HashMap<>();
        map.put("m", 0);
        map.put("n", 5);
        List<Student> list = sqlSession.selectList("mapper.StudentMapper.findByPage", map);
        for (Student student : list) {
            System.out.println(student);
        }
    }

}

结果

testInsert()的结果

2019-02-16 22:41:18 [DEBUG] mapper.StudentMapper.abc - ==>  Preparing: insert into student(sid, sname, birthday, sex) values ( null, ?, ?, ?) 
2019-02-16 22:41:19 [DEBUG] mapper.StudentMapper.abc - ==> Parameters: 李五(String), 2019-02-16 22:41:13.8(Timestamp), 男(String)
2019-02-16 22:41:21 [DEBUG] mapper.StudentMapper.abc - <==    Updates: 1
sid的值:1009

testDelete()的结果

2019-02-16 22:51:16 [DEBUG] mapper.StudentMapper.delete - ==>  Preparing: delete from student where sid = ? 
2019-02-16 22:51:17 [DEBUG] mapper.StudentMapper.delete - ==> Parameters: 1009(Integer)
2019-02-16 22:51:17 [DEBUG] mapper.StudentMapper.delete - <==    Updates: 1

testFindAll()的结果

2019-02-16 22:47:38 [DEBUG] mapper.StudentMapper.findAll - ==>  Preparing: select sid,sname name,birthday,sex from student 
2019-02-16 22:47:38 [DEBUG] mapper.StudentMapper.findAll - ==> Parameters: 
2019-02-16 22:47:39 [DEBUG] mapper.StudentMapper.findAll - <==      Total: 9
Student{sid=1001, sname='张三', birthday=Wed Oct 10 00:00:00 CST 1990, sex='男'}
Student{sid=1002, sname='李四', birthday=Sat Oct 10 00:00:00 CST 1981, sex='男'}
Student{sid=1003, sname='王五', birthday=Tue Nov 10 00:00:00 CST 1981, sex='女'}
Student{sid=1004, sname='赵六', birthday=Mon Oct 10 00:00:00 CST 1988, sex='男'}
Student{sid=1005, sname='孙七', birthday=Tue Jan 10 00:00:00 CST 1989, sex='女'}
Student{sid=1006, sname='周八', birthday=Wed Oct 10 00:00:00 CST 1990, sex='男'}
Student{sid=1007, sname='张三', birthday=Sun Jun 10 00:00:00 CDT 1990, sex='女'}
Student{sid=1008, sname='张丽', birthday=Fri Dec 11 00:00:00 CST 1998, sex='女'}
Student{sid=1009, sname='李五', birthday=Sat Feb 16 22:41:14 CST 2019, sex='男'}


testFindById()的结果

2019-02-16 22:48:32 [DEBUG] mapper.StudentMapper.findById - ==>  Preparing: select sid,sname name,birthday,sex from student where sid = ? 
2019-02-16 22:48:32 [DEBUG] mapper.StudentMapper.findById - ==> Parameters: 1001(Integer)
2019-02-16 22:48:33 [DEBUG] mapper.StudentMapper.findById - <==      Total: 1
Student{sid=1001, sname='张三', birthday=Wed Oct 10 00:00:00 CST 1990, sex='男'}

testUpdate()的结果

2019-02-16 22:49:18 [DEBUG] mapper.StudentMapper.update - ==>  Preparing: update student SET sex=? where sid=? 
2019-02-16 22:49:18 [DEBUG] mapper.StudentMapper.update - ==> Parameters: 男(String), 1001(Integer)
2019-02-16 22:49:18 [DEBUG] mapper.StudentMapper.update - <==    Updates: 1

testFindByPage()的结果

2019-02-16 22:50:00 [DEBUG] mapper.StudentMapper.findByPage - ==>  Preparing: select sid,sname name,birthday,sex from student limit ?, ? 
2019-02-16 22:50:01 [DEBUG] mapper.StudentMapper.findByPage - ==> Parameters: 0(Integer), 5(Integer)
2019-02-16 22:50:01 [DEBUG] mapper.StudentMapper.findByPage - <==      Total: 5
Student{sid=1001, sname='张三', birthday=Wed Oct 10 00:00:00 CST 1990, sex='男'}
Student{sid=1002, sname='李四', birthday=Sat Oct 10 00:00:00 CST 1981, sex='男'}
Student{sid=1003, sname='王五', birthday=Tue Nov 10 00:00:00 CST 1981, sex='女'}
Student{sid=1004, sname='赵六', birthday=Mon Oct 10 00:00:00 CST 1988, sex='男'}
Student{sid=1005, sname='孙七', birthday=Tue Jan 10 00:00:00 CST 1989, sex='女'}

每个结果最上面都会生成sql语句,#{}代替的地方会出现?,因为它底层用到prepareStatement,并且显示出一些信息,比如:有几条结果 Total、参数 Paramteters、更新了几条 update ,这是配置logback才会出现的,这样就容易观察到SQL语句是否错误,当然也是要添加依赖的,具体步骤上面已经说过了。

2、接口标签增删改

存储学生信息的代码

import java.util.Date;

public class Student {

    private int sid;
    private String sname;
    private Date birthday;

    private String sex;

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                '}';
    }
}

标签增删改接口

import domain.Student;
import org.apache.ibatis.annotations.*;

import java.util.List;
import java.util.Map;

public interface StudentMapper {

    @Insert("insert into student(sid,sname,birthday,sex) values(null,#{sname}, #{birthday}, #{sex})")
    @Options(useGeneratedKeys = true, keyProperty = "sid")
    void insert(Student student);

    @Update("update student set sname=#{sname}, birthday=#{birthday}, sex=#{sex} where sid=#{sid}")
    void update(Student student);

    @Delete("delete from student where sid=#{sid}")
    void delete(int sid);

    @Select("select * from student")
    List<Student> findAll();

    @Select("select * from student where sid = #{sid}")
    Student findById(int sid);

    @Select("select * from student limit #{m}, #{n}")
    List<Student> findByPage(Map map);

    // 同时根据姓名和性别查询
    @Select("select * from student where sname=#{a} and sex=#{b}")
    List<Student> find1(@Param("a") String sname, @Param("b") String sex);

    /*
    方法不能重载,下面这语句会出错
    @Select("select * from student where sname=#{a}")
    List<Student> find1(String sname);*/

    @Select("select * from student where sname=#{a} and sex=#{b}")
    List<Student> find2(Map<String, Object> map);


    void deleteByIds(List<Integer> ids);

}

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"/>
            <!-- 配置了 mybatis 连接数据库的信息 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <!-- jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf8
                 & ==> &amp;
                -->
                <property name="url" value="jdbc:mysql://localhost:3306/school?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 指定映射接口的 包名.类名 ,也就是接口的位置 接口是使用class-->
        <mapper class="mapper.StudentMapper"></mapper>
    </mappers>
</configuration>

测试类

import domain.Student;
import mapper.StudentMapper;
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.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class TestStudentMapper {
    static SqlSessionFactory factory;
    static {
        // 读取配置文件
        try(InputStream in = Resources.getResourceAsStream("mybatis-config.xml")){
            // 创建 sqlSession 工厂类
            factory = new SqlSessionFactoryBuilder().build(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Test
    public void testInsert() throws ParseException {
        SqlSession sqlSession = factory.openSession();
        // 先通过 sqlSession 获得接口对象
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        Student stu = new Student();
        stu.setSname("啊啊啊");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

        stu.setBirthday(sdf.parse("1999-5-5"));
        stu.setSex("男");
        // 通过接口对象中声明的方法 执行sql语句
        mapper.insert(stu);

        sqlSession.commit();
        sqlSession.close();
    }

    @Test
    public void testFindAll() {
        SqlSession sqlSession = factory.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> list = mapper.findAll();
        for (Student student : list) {
            System.out.println(student);
        }
        sqlSession.close();
    }

    @Test
    public void testFindByPage() {
        SqlSession sqlSession = factory.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        Map<String, Object> map = new HashMap<>();
        map.put("m", 0);
        map.put("n", 5);
        List<Student> list = mapper.findByPage(map);
        System.out.println("mapper 实际类型是:" + mapper.getClass());

        for (Student student : list) {
            System.out.println(student);
        }

        sqlSession.close();
    }

    @Test
    public void testFind2() {
        SqlSession sqlSession = factory.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Map<String, Object> map = new HashMap<>();
        map.put("a", "张三");
        map.put("b", "男");
        List<Student> list = mapper.find2(map);
        for (Student student : list) {
            System.out.println(student);
        }
    }

    @Test
    public void testFind1() {
        SqlSession sqlSession = factory.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        List<Student> list = mapper.find1("张三", "男");
        for (Student student : list) {
            System.out.println(student);
        }
    }

    @Test
    public void testDeleteByIds() {
        SqlSession sqlSession = factory.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        mapper.deleteByIds(Arrays.asList(1013));

        sqlSession.commit();
        sqlSession.close();
    }
}

和上面代码相同的结果都是一样的,其他的结过也差不多,注解方法和xml方法都可以使代码简洁,不会像以前那样需要些很多那些代码,在代码量上简洁不少,并且mybatis-config.xml文件中接口用的是class、xml文件用的是resource。

总结

mybatis的作用还有很多,需要继续学习,这个是 http://www.mybatis.org/mybatis-3/zh/index.html  官方文档地址(偏向基础),想要了解原理要阅读源码,在应用上更近一步学习 mybatis-plus (https://mp.baomidou.com/ ) 是mybatis的扩展 ,这个持久层框架配置有点多,一不下心容易出错,因此要学会如何找出错误处 ,出错时会有提示哪里出错,要多多关注,错多了,也就知道哪里出错了,可以说是错处了手感,哈哈哈哈。

猜你喜欢

转载自blog.csdn.net/grey_mouse/article/details/87394846