MyBatis总结

1、介绍

MyBatis 是⼀个持久层框架,它⽀持定制化 SQL、存储过程以及⾼级映射。

MyBatis 避免了⼏乎所有的 JDBC 代码和⼿动设置参数以及获取结果集。

MyBatis 可以使⽤简单的 XML 或注解来配置和映射原⽣信息,将接⼝和 Java POJO(普通的Java对象)映射成数据库中的记录。

MyBatis是一个半自动的ORM框架(Object Relation Mapping)

和其他持久化框架比较:

2、搭建Mybatis

1、导包

<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.40</version>
</dependency>

2、添加配置文件:mybatis-config.xml

        注:整合spring后可省略,创建位置在src/main/resource

(1).指定连接数据库的url,username,password,driver

(2).指定了事务的管理对象

<?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>
<!--链接数据库环境,default要和id值相同,default表示默认访问环境,-->
    <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://localhost:3306/数据库名?useUnicode=true&amp;characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value=""/>
            </dataSource>
        </environment>
    </environments>
    <!-- 指定maper⽂件的路径(maven项⽬从resources源⽂件夹下找资源)-->
    <mappers>
        <mapper resource="mapper⽂件名"/>
    </mappers>
</configuration>

3、创建实体类和接口

4、在resource下面,添加mapper文件(保存SQL语句)

<?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="com.vv.dao.StudentDao">
    <!--id名和方法名一样-->
    <select id="getAll" resultType="com.vv.bean.Student">
        select * from student
    </select>
</mapper>

5、获得SqlSession,通过该对象进⾏数据的操作

public class Test {
    public static void main(String[] args) {
        try {
            //加载配置文件
            Reader resourceAsReader = Resources.getResourceAsReader("mybatis-config.xml");
            //获得SqlSessionFactoryBuilder
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = builder.build(resourceAsReader);
            //得到sqlSession
            SqlSession sqlSession = build.openSession();
            //操作sql,参数:sql完整路径.方法
            List<Student> list = sqlSession.selectList("com.vv.dao.StudentDao.getAll");
            for (Student student : list) {
                System.out.println(student.toString());
            }
            //关闭资源
            sqlSession.close();
            resourceAsReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

CRUD

接口:

public interface StudentDao {
    //查询所有
    public List<Student> getAll();
    //主键查询
    public Student findById(int id);
    //查询最大值、最小值等
    public Map find();
    //新增
    public int add(Student student);
    //新增2(传递多个参数用Map)
    public int add2(Map map);
    //删除
    public int deleteById(int id);
    //修改
    public int update(Student student);
}

mapper:

<mapper namespace="com.vv.dao.StudentDao">
    <!--id名和方法名一样-->
    <select id="getAll" resultType="com.vv.bean.Student">
        select * from student
    </select>
    <!--主键查询-->
    <select id="findById" parameterType="int" resultType="com.vv.bean.Student">
        select * from student where sid = #{id}
    </select>
    <!--查询最大值、最小值等,处理结果没有和实体类做对应的时候,可以返回map类型-->
    <select id="find" resultType="map">
        select MAX(age) max , MIN(age) min from student
    </select>
    <!--增删改,返回的是受影响的行数,不需要配置resultType-->
    <!--新增-->
    <insert id="add" parameterType="com.vv.bean.Student">
        insert into student values (#{sid},#{name},#{age})
    </insert>
    <!--多参数情况新增-->
    <insert id="add2" parameterType="com.vv.bean.Student">
        insert into student values (#{sid},#{name},#{age})
    </insert>
    <!--删除-->
    <delete id="deleteById" parameterType="int">
        delete from student where sid = #{id}
    </delete>
    <!--修改-->
    <update id="update" parameterType="com.vv.bean.Student">
        update student set name = #{name} , age = #{age} where sid = #{sid}
    </update>
</mapper>

方式一: SqlSession 发送 SQL

获得新增数据的id(主键自增)

方式二:用 Mapper 接口发送 SQL

        通过 SqlSession 的 getMapper 方法来获取一个 Mapper 接口,就可以调用它的方法了。因为 XML 文件或者接口注解定义的 SQL 都可以通过“类的全限定名+方法名”查找,所以 MyBatis 会启用对应的 SQL 进行运行,并返回结果。

上面分别展示了 MyBatis 存在的两种发送 SQL 的方式,一种用 SqlSession 直接发送,另外一种通过 SqlSession 获取 Mapper 接口再发送。建议采用 SqlSession 获取 Mapper 的方式。

使用 Mapper 接口编程可以消除 SqlSession 带来的功能性代码,提高可读性。使用 Mapper 接口,类似 dao.getAll则是完全面向对象的语言,更能体现业务的逻辑。

使用 dao.getAll方式,IDE 会提示错误和校验,而使用 sqlSession.selectOne(“getAll”)语法,只有在运行中才能知道是否会产生错误。

目前使用 Mapper 接口编程已成为主流,尤其在 Spring 中运用 MyBatis 时,Mapper 接口的使用就更为简单。

SqlSession管理

ThreadLocal 处理 sqlSession

ThreadLocal并⾮是⼀个线程的本地实现版本,它并不是⼀个Thread,⽽是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功⽤⾮常简单,就是为每⼀个使⽤该变量的线程都提供⼀个变量值的副本,是Java中⼀种较为特殊的线程绑定机制,是每⼀个线程都可以独⽴地改变⾃⼰的副本,⽽不会和其它线程的副本冲突。

SqlSessionUtil 

public class SqlSessionUtil {
    private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<>();
    private static SqlSessionFactory sqlSessionFactory;
    /**
     * 加载配置文件
     */
    static {
        try {
            Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        }catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    /**
     * 获取SqlSession
     */
    public static SqlSession getSqlSession() {
        //从当前线程获得
        SqlSession sqlSession = threadLocal.get();
        if(sqlSession==null) {
            //若没有,则创建并且绑定当前线程
            sqlSession = sqlSessionFactory.openSession();
            threadLocal.set(sqlSession);
        }
        return sqlSession;
    }
    /**
     * 关闭SqlSession
     */
    public static void closeSqlSession() {
        SqlSession sqlSession = threadLocal.get();
        if(sqlSession!=null) {
            sqlSession.close();
            threadLocal.remove();
        }
    }
}

给实体类起别名

为了方便类名的管理,可以使用起别名的方式

定义在mybatis-config.xml的configuration中

<!--起别名-->
<typeAliases>
    <!--给类起别名-->
    <typeAlias type="com.vv.bean.Student" alias="stu"></typeAlias>
    <!--指定哪些包的类可以使⽤别名,默认别名:类名⾸字⺟⼩写(实际使⽤的时候,全部⼩写也可以做结果映射)-->
    <package name="com.vv.bean"/><!--com.vv.bean.Student 的别名就是student-->
</typeAliases>

log4j ⽇志记录

1、导包

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.5</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.12</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

2、创建log4j.properties

log4j.rootLogger=DEBUG, Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n

log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

后面运行sql语句后,就可以在控制台看到日志信息了

复杂查询

1、in查询

foreach标签中属性说明:

item 表示集合中每⼀个元素进⾏迭代时的别名,等同于c 标签中的var

index 指定⼀个名字,⽤于表示在迭代过程中,每次迭代到的位置,可以不写

open 表示该语句以什么开始,

separator 表示在每次进⾏迭代之间以什么符号作为分隔符,

close 表示以什么结束,

注意:在使⽤foreach 的时候最关键的也是最容易出错的就是collection 属性,collection该属性是必须指定的,list时取值list,数组时取值array,map 时取值mapkey

接口

public interface StudentDao2 {
    /**
     * in查询
     * @param ids  要查询的id集合
     * @return      返回学生的集合
     */
    public List<Student> find1(List ids);
    public List<Student> find2(int[] ids);
    public List<Student> find3(Map map);
}

Mapper:

<!--参数是list集合-->
<select id="find1" resultType="stu">
    select * from student where sid in
    <foreach collection="list" item="sid" open="(" close=")" separator=",">
        #{sid}
    </foreach>
</select>
public class TestIn {
    public static void main(String[] args) {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        StudentDao2 dao = sqlSession.getMapper(StudentDao2.class);
        List ids = new ArrayList();
        ids.add(1);
        ids.add(2);
        ids.add(4);
        List<Student> list = dao.find1(ids);
        for (Student student : list) {
            System.out.println(student);
        }
        sqlSession.close();
    }
}
<!--参数是数组-->
<select id="find2" resultType="stu">
    select * from student where sid in
    <foreach collection="array" item="sid" open="(" close=")" separator=",">
        #{sid}
    </foreach>
</select>
public class TestIn {
    public static void main(String[] args) {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        StudentDao2 dao = sqlSession.getMapper(StudentDao2.class);
        int[] ids = {1,2,4};
        List<Student> list = dao.find2(ids);
        for (Student student : list) {
            System.out.println(student);
        }
        sqlSession.close();
    }
}
<!--参数是map,collection写key值-->
<select id="find3" resultType="stu">
    select * from student where sid in
    <foreach collection="ids" item="sid" open="(" close=")" separator=",">
        #{sid}
    </foreach>
</select>
public class TestIn {
    public static void main(String[] args) {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        StudentDao2 dao = sqlSession.getMapper(StudentDao2.class);
        List ids = new ArrayList();
        ids.add(1);
        ids.add(2);
        ids.add(4);
        Map map = new HashMap();
        map.put("ids",ids);
        List<Student> list = dao.find3(map);
        for (Student student : list) {
            System.out.println(student);
        }
        sqlSession.close();
    }
}

2、动态sql模糊查询

<if test="属性名!=属性值">

    and name like '${属性名}'   或者  and name like "%"#{username}"%"

</if>

注意:test属性中读取属性值时直接写属性名

多个条件时使⽤and,or 拼接

如果传递过来的是map类型,则test属性中写的是key

#{}:相当于占位符

#{id}:其中的id可以表示输⼊参数的名称,如果是简单类型名称可以任意

${}:表示拼接sql语句

${value}:表示获取输⼊的参数值,${}会引起SQL注⼊,⼀般情况下不推荐使⽤。

接口:

public interface StudentDao2 {
    /**
     * 模糊查询,根据学生姓名和年龄进行查询
     * @param map
     * @return
     */
    public List<Student> find4(Map map);
    public List<Student> find5(Student student);
}
<!--参数是map,取值的时候用#{key值}-->
<select id="find4" resultType="Student">
    select * from student where 1=1  <!--防止后面and语法出错-->
    <if test="keyName!=null and keyName!=''">
        and name like "%"#{keyName}"%"
    </if>
    <if test="keyAge!=null and keyAge!=''">
        and age>=#{keyAge}
    </if>
</select>
public class TestLike {
    public static void main(String[] args) {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        StudentDao2 dao = sqlSession.getMapper(StudentDao2.class);
        Map map = new HashMap();
        map.put("keyName","杨");
        map.put("keyAge",18);//查询姓杨,年龄大于等于18
        List<Student> students = dao.find4(map);
        for (Student student : students) {
            System.out.println(student);
        }
    }
}

<!--参数是对象,其中的name和age是对象的属性-->
<select id="find5" resultType="Student">
    select * from student where 1=1  <!--防止后面and语法出错-->
    <if test="name!=null and name!=''">
        and name like "%"#{name}"%"
    </if>
    <if test="age!=null and age!=''">
        and age>=#{age}
    </if>
</select>
public class TestLike {
    public static void main(String[] args) {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        StudentDao2 dao = sqlSession.getMapper(StudentDao2.class);
        Student stu = new Student();
        stu.setName("杨");
        stu.setAge(18);
        List<Student> students = dao.find5(stu);
        for (Student student : students) {
            System.out.println(student);
        }
    }
}

3、区间查询

接口:

public interface StudentDao2 {
    /**
     * 查询sid在2-4的学生
     * @param map
     * @return
     */
    public List<Student> find6(Map map);
}
<select id="find6" resultType="Student">
    <!--select * from student where sid between #{begin} and #{end}-->
    select * from student where sid >= #{begin} and sid <![CDATA[ <= ]]> #{end} <!--这里会误认为<是个标签-->
</select>
public class TestBetween {
    public static void main(String[] args) {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        StudentDao2 dao = sqlSession.getMapper(StudentDao2.class);
        Map map = new HashMap();
        map.put("begin",2);
        map.put("end",4);
        List<Student> students = dao.find6(map);
        for (Student student : students) {
            System.out.println(student);
        }
        sqlSession.close();
    }
}

4、resultMap:查询结果映射

(1)处理单表关系

当列名和属性名不一致的时候,通过给列起别名,让别名=属性名,也可以实现数据对应(resultMap的property属性,对应实体类的属性,column对应数据表的列)

resultType="指定返回值的类型"(当列名和属性名⼀致时使⽤)

resultMap="key " (当列名和属性名不⼀致做多表查询时)

<resultMap id="aaa" type="bean.Dept"> 
 <!-- 可以⼿动指定列名和属性名的关系 ,⾮主键列使⽤result 标签,主键列使⽤id 标签--> 
    <id property="id" column="sid"></id> 
    <result property="stuName" column="name"/> 
    <result property="stuAge" column="age"/> 
</resultMap>

(2)处理多表关系

两表联查:⼀对多和多对⼀

:如果是单表查询,select 中使⽤resultType 设置返回的类型即可,但是如果是多表联查,那么select 查询的结果需要单独使⽤resultMap 标签来进⾏结果的映射

存的是集合的话使⽤collection ⼦标签,指定对象类型用ofType

存的是一方的话使⽤association ⼦标签,指定对象类型用javaType

resultType resultMap 属性只能出现⼀个

一对多:

存年级的实体类(包含学生的集合)

接口

mapper

<resultMap id="rs1" type="com.vv.bean.Grade">
    <!--先描述自身的信息,再秒速关联表的信息-->
    <id property="gid" column="gid"/>
    <result property="gname" column="gname"/>
    <collection property="students" ofType="com.vv.bean.Student">
        <id property="sid" column="sid"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
    </collection>
</resultMap>
<select id="findByGid" resultMap="rs1">
    SELECT * FROM grade,student where grade.gid = student.gradeid and grade.gid=#{id}
</select>

多对一:

<resultMap id="rs2" type="com.vv.bean.Student">
    <id property="sid" column="sid"/>
    <result property="name" column="name"/>
    <result property="age" column="age"/>
    <association property="grade" javaType="com.vv.bean.Grade">
        <id property="gid" column="gid"/>
        <result property="gname" column="gname"/>
    </association>
</resultMap>
<select id="findAllStudent" resultMap="rs2">
    SELECT * FROM grade,student where grade.gid = student.gradeid
</select>

pageHelper分页

sql 语句只需要查询数据,不实现分⻚代码

方式一:

Mybatis使⽤RowBounds对象进⾏分页,它是针对ResultSet结果集执行的内存分页,不是物理分页。

方式二:分页插件

分⻚插件的基本原理是使⽤Mybatis提供的插件接⼝,实现⾃定义插件,在插件的拦截⽅法内拦截待执⾏的sql,然后重写sql,根据dialect⽅⾔,添加对应的物理分⻚语句和物理分⻚参数。

(1)导包

 <dependency>
     <groupId>com.github.pagehelper</groupId>
     <artifactId>pagehelper</artifactId>
     <version>5.1.6</version>
 </dependency>

(2)在mybatis-config.xml配置数据库环境之前指定

<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>

(3)调用方法

//指定分页参数
PageHelper.startPage(2,3);//页码和每页大小
//调取dao方法
List<Student> list = studentDao.getAll();
//创建分页工具类对象
PageInfo<Student> studentPageInfo = new PageInfo<>(list);
//从分页数据中获得数据
for (Student student : studentPageInfo.getList()) {
    System.out.println(student);
}
System.out.println("当前页条数:"+studentPageInfo.getSize());
System.out.println("总页条数:"+studentPageInfo.getTotal());
System.out.println("总页数:"+studentPageInfo.getPages());
System.out.println("上一页:"+studentPageInfo.getPrePage());//如果没有,则返回0
System.out.println("下一页:"+studentPageInfo.getNextPage());
System.out.println("当前页:"+studentPageInfo.getPageNum());
System.out.println("显示条数:"+studentPageInfo.getPageSize());

优缺点

物理分页每次都要访问数据库,逻辑分页只访问⼀次

物理分页占⽤内存少,逻辑分页相对较多

物理分页数据每次都是最新的,逻辑分页有可能滞后

缓存

⼀级缓存(同一个sqlSession查询相同的结果,第二次就直接从缓存中获取,一旦sqlSession关闭,缓存就不存在了)

SqlSession 的缓存 ------>⾃动开启

⼆级缓存:

做到从不同的缓存中共享数据(同一个SqlSessionFactory创造的sqlSession都有效

SqlSessionFactory 的缓存 --->需要⼿动开启

映射配置⽂件中配置

<mapper namespace="接⼝路径"> 
    <cache eviction="FIFO"     
        flushInterval="60000" 
        size="512" 
        readOnly="true"/> 
</mapper>

 eviction: ⼆级缓存中,缓存的对象从缓存中移除的策略,回收策略为先进先出
 flushInterval: 刷新缓存的事件间隔,单位:毫秒 
 size: 缓存对象的个数
 readOnly: 是否是只读的

cache元素属性设置如下:

flushInterval:刷新间隔,可以被设置为任意的正整数,⽽且它们代表⼀个合理的毫秒形式的时间段,默认情况下是不设置的,也就是没有刷新间隔,缓存仅仅调⽤语句时刷新。

size:缓存数⽬,可以被设置为任意正整数,要记住你的缓存对象数⽬和你运⾏环境可⽤内存资源数⽬,默认值是1024.

readOnly:只读,属性可以被设置为truefalse,只读的缓存会给所有调⽤者返回缓存对象的相同实例,因此这些对象不能被修改。这提供了很重要的性能优势,可读写的缓存会返回缓存对象的拷⻉(通过序列化),这会慢⼀些,但是安全,因此默认是false

eviction:收回策略,默认为LRU,有如下⼏种:

LRU:最近最少使⽤的策略,移除最⻓时间不被使⽤的对象。

FIFO:先进先出策略,按对象进⼊缓存的顺序来移除它们。

SOFT:软引⽤策略,移除基于垃圾回收器状态和软引⽤规则的对象。

WEAK:弱引⽤策略,更积极地移除基于垃圾收集器状态和弱引⽤规则的对象。

注意:使⽤⼆级缓存时,与查询结果映射的java对象必须实现java.io.Serializable接⼝的序列化和反序列化操作,如果存在⽗类,其成员都需要实现序列化接⼝,实现序列化接⼝是为了对缓存数据进⾏序列化和反序列化操作,因为⼆级缓存数据存储介质多种多样,不⼀定在内存,有可能是硬盘或者远程服务器。

Mybatis注解

方式一:在接口方法上面直接添加

记住提要要在mybatis-config.xml配置

<mappers>
    <mapper class="com.vv.dao.StudentDao"/>
</mappers>
public interface StudentDao {
    //查询所有
    @Select("select * from student")
    public List<Student> getAll();

    //查询最大值、最小值等(返回是个map,调用map.get("max")就行了)
    @Select("select max(age) max,min(age) min from student")
    public Map find();

    //查找总条数(返回的int就是total)
    @Select("select count(*) from student")
    public int totalCount();

    //新增
    @Insert("insert into student (name,age) values (#{name},#{age})")
    @Options(useGeneratedKeys = true,keyProperty = "sid")//获得新增数据id
    public int add(Student student);

    //删除
    @Delete("delete from student where sid=#{sid}")
    public int deleteById(int id);

    //修改
    @Update("update student set name=#{name},age=#{age} where sid=#{sid}")
    public int update(Student student);
}

当列名和属性名不一致的时候

@Select("select * from student")
@Results({
    @Result(id = true, property = "id", column = "test_id")//主键属性,在前面只需要加id=true
    @Result(column = "username",property = "user_name")
})

多表查询的时候:

public interface GradeDao {
    //查询学生信息及对应的年级信息(两表联查,需要使用ResultMap)
    @ResultMap({"com.vv.dao.GradeDao.rs1"})
    @Select("SELECT * FROM grade,student where grade.gid = student.gradeid")
    public List<Student> findAllStudent();
}
<mapper namespace="com.vv.dao.GradeDao">
    <resultMap id="rs1" type="com.vv.bean.Student">
        <id property="sid" column="sid"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
        <association property="grade" javaType="com.vv.bean.Grade">
            <id property="gid" column="gid"/>
            <result property="gname" column="gname"/>
        </association>
    </resultMap>
</mapper>

新增的时候不是传的对象,而是几个参数的时候

@Insert("insert into student (name,age,gradeid) values (#{name},#{age},#{gradeid})")
public int addParam(@Param("name") String name,@Param("age") int age,@Param("gradeid") int gradeid);

开启二级缓存

@CacheNamespace//需要和@CacheNamespace⼀起使⽤,并且对象需要实现序列化接⼝
//@CacheNamespace(size = 512) : 定义在该命名空间内允许使⽤内置缓存,最⼤值为512个对象引⽤,
//读写默认是开启的,
//缓存内省刷新时间为默认3600000毫秒
public interface StudentDao {
    //查询所有
    @Options(useCache = true,
            flushCache = Options.FlushCachePolicy.FALSE, //表示查询时不刷新缓
            timeout = 999999) //表示查询结果缓存10000秒
    @Select("select * from student")
    public List<Student> getAll();

}

动态sql建议在xml中定义,方便调整

方式二:抽取到一个工具类里面

/**
 * 定义SQL的工具类,返回是个字符串
 */
public class SqlUtil {
    public String getAllMethod(){
        return "select * from student";
    }
    public String findMethod(){
        return "select max(age) max,min(age) min from student";
    }
    public String totalCountMethod(){
        return "select count(*) from student";
    }
    public String addMethod(){
        return "insert into student (name,age) values (#{name},#{age})";
    }
    public String updateMethod(){
        return "update student set name=#{name},age=#{age} where sid=#{sid}";
    }
    public String deleteMethod(){
        return "delete from student where sid=#{sid}";
    }
}
public interface StudentDao {
    //查询所有
    @SelectProvider(type = SqlUtil.class,method = "getAllMethod")
    public List<Student> getAll();

    //查询最大值、最小值等(返回是个map,调用map.get("max")就行了)
    @SelectProvider(type = SqlUtil.class,method = "findMethod")
    public Map find();

    //查找总条数(返回的int就是total)
    @SelectProvider(type = SqlUtil.class,method = "totalCountMethod")
    public int totalCount();

    //新增
    @InsertProvider(type = SqlUtil.class,method = "addMethod")
    @Options(useGeneratedKeys = true,keyProperty = "sid")//获得新增数据id
    public int add(Student student);

    //删除
    @DeleteProvider(type = SqlUtil.class,method = "deleteMethod")
    public int deleteById(int id);

    //修改
    @UpdateProvider(type = SqlUtil.class,method = "updateMethod")
    public int update(Student student);
}

lombok使用

先导包

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.2</version>
    <scope>provided</scope>
</dependency>

@Data 注解在类上;提供类所有属性的 getting setting ⽅法,此外还提供了equalscanEqual、hashCode、toString ⽅法

@Setter :注解在属性上;为属性提供 setting ⽅法

@Getter :注解在属性上;为属性提供 getting ⽅法

@Log4j :注解在类上;为类提供⼀个 属性名为log log4j ⽇志对象

@NoArgsConstructor :注解在类上;为类提供⼀个⽆参的构造⽅法

@AllArgsConstructor :注解在类上;为类提供⼀个全参的构造⽅法

@Cleanup : 可以关闭流

@Builder : 被注解的类加个构造者模式

@Synchronized : 加个同步锁

@SneakyThrows : 等同于try/catch 捕获异常

@NonNull : 如果给参数加个这个注解 参数为null会抛出空指针异常

@Value : 注解和@Data类似,区别在于它会把所有成员变量默认定义为private final修饰,并且不会⽣成set⽅法。

@ToString 重写toString()⽅法

Mabatis自动化

作⽤:反向⽣成实体类,接⼝,mapper.xml

1、导包

<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.5</version>
</dependency>

2、加载插件(添加到dependencies下面)

<build>
    <plugins>
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.5</version>
            <configuration>
                <!--配置⽂件的路径-->
                <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
                <overwrite>true</overwrite>
            </configuration>
            <dependencies>
                <dependency>
                    <groupId>org.mybatis.generator</groupId>
                    <artifactId>mybatis-generator-core</artifactId>
                    <version>1.3.5</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

3、配置文件generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<!-- 配置⽣成器 -->
<generatorConfiguration>
    <!--数据库驱动jar(在maven中找) -->
    <classPathEntry
            location="D:\maven-repo\mysql\mysql-connector-java\5.1.40\mysql-connector-java-5.1.40.jar"/>
    <context id="MyBatis" targetRuntime="MyBatis3">
        <!--去除注释 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!--数据库连接 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/mybatistest"
                        userId="root"
                        password="">
        </jdbcConnection>
        <!--⽣成实体类 指定包名 以及⽣成的地址 (可以⾃定义地址,但是路径不存在不会⾃动创建
        使⽤Maven⽣成在target⽬录下,会⾃动创建) -->
        <javaModelGenerator targetPackage="com.vv.bean"

                            targetProject="D:\IDEA\Mybatis\src\main\java">
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!--⽣成SQLmapper⽂件 -->
        <sqlMapGenerator targetPackage="mapper"

                         targetProject="D:\IDEA\Mybatis\src\main\resources">
        </sqlMapGenerator>
        <!--⽣成Dao⽂件,⽣成接⼝ -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.vv.dao"
                             targetProject="D:\IDEA\Mybatis\src\main\java">
        </javaClientGenerator>
        <!--指定表-->
        <table tableName="student" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false"
               enableSelectByExample="false" selectByExampleQueryId="false">
        </table>
        <table tableName="grade" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false"
               enableSelectByExample="false" selectByExampleQueryId="false">
        </table>
    </context>
</generatorConfiguration>

4、运⾏:maven Project选项卡->plugins->找到mybatis-generator-core,双击运⾏就会⾃动⽣成

注意:运⾏⼀次即可,如果运⾏过程中,未完全成功。则将原来⽣成的代码删除后,再次运⾏。

如果是web项目需要先:设置Command line:mybatis-generator:generate -e

然后运行

Mybatis Plus

Mybatis-Plus (简称 MP )是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,避免了我们重复CRUD 语句

快速入门

创建项目,添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.vv</groupId>
    <artifactId>MybatisPlus-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <mybatisplus.version>3.3.2</mybatisplus.version>
        <skipTests>true</skipTests>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!--这里使用的是h2数据库-->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatisplus.version}</version>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId> <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

配置文件

spring:
  datasource:
    driver-class-name: org.h2.Driver
    schema: classpath:db/schema-h2.sql
    data: classpath:db/data-h2.sql
    url: jdbc:h2:mem:test
    username: root
    password: test

logging:
  level:
    com.vv: debug

mybatis-plus:
  mapper-locations: classpath:/mapper/*.xml

编写mapper接口,里面已经自动实现了CRUD等操作

/**
 * 该接口自动实现 一些CRUD的操作
 */
public interface UserMapper extends BaseMapper<User> {
}

编写完实体类后,并且在启动类上加@MapperScan的注解,然后测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
    @Autowired
    public UserMapper mapper;

    @Test
    public void selectAll(){
        /**
         * 查询所有
         */
        List<User> users = mapper.selectList(null);
        for (User user : users) {
            System.out.println(user);
        }
    }
}

常用注解

MyBatisPlus 提供了一些注解供我们在实体类和表信息出现不对应的时候使用。通过使用注解完成逻辑上匹配。

@Table  //默认主键策略是:采用雪花算法生成全局唯一主键

@TableId(type = IdType.AUTO) //自增

内置CRUD

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
    @Autowired
    public UserMapper mapper;

    /**
     * 添加
     */
    @Test
    public void insert(){
        User user = new User();
        user.setAge(18);
        user.setName("vv");
        user.setEmail("1563499446@qq.com");
        if(mapper.insert(user) > 0){
            //查询所有
            mapper.selectList(null).forEach(System.out :: println);
        }
    }

    /**
     * 删除
     */
    @Test
    public void delete(){
        //主键删除
        if(mapper.deleteById(2L) > 0) {
            mapper.selectList(null).forEach(System.out :: println);
        }

        //批量删除:方法一
        mapper.delete(new QueryWrapper<User>().like("name","J"));//删除name  含有J的数据

        //批量删除:方法二
        mapper.delete(Wrappers.<User>query().like("name", "J"));

        //批量删除:方法三
        mapper.delete(Wrappers.<User>query().lambda().like(User::getName, "J"));
    }

    /**
     * 修改
     */
    @Test
    public void update(){
        //在实体类使用lombok时,添加@Accessors(chain = true),  set方法返回实体对象

        //修改
        mapper.updateById(new User().setId(1l).setName("慧科"));

        //批量修改,和上面的删除差不多
        mapper.update(null, Wrappers.<User>update().set("email", "huike@163.com").like("name", "J"));
        //或者
        mapper.update(new User().setEmail("huike@163.com"), Wrappers.<User>update().like("name", "J"));
        mapper.selectList(null).forEach(System.out :: println);
    }

    /**
     * 查找
     */
    @Test
    public void select(){
        //查询所有
        //mapper.selectList(null);

        //查询name=Tom
        //User user1 = mapper.selectOne(Wrappers.<User>query().eq("name", "Tom"));

        //投影查询(只查询指定的字段数据,其他字段为null)
        List<User> users = mapper.selectList(new QueryWrapper<User>().select("id", "name"));
        for (User user : users) {
            System.out.println(user);
        }
    }
}

分页

创建配置类

@Configuration
public class MybatisPlusConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        // 开启 count 的 join 优化,只针对 left join
        return new PaginationInterceptor().setCountSqlParser(new JsqlParserCountOptimize(true));
    }
}

内置分页

    /**
     * 内置分页
     */
    @Test
    public void page1() {
        /**
         * 参数1:第几页
         * 参数2:每页条数
         */
        Page<User> page = new Page<>(1, 5);

        Page<User> pages = mapper.selectPage(page, Wrappers.<User>query());
        //也可以条件分页查询
        //Page<User> pageResult = mapper.selectPage(page, new QueryWrapper<User>().eq("age", 20));
        System.out.println("总条数 ------> " + pages.getTotal());
        System.out.println("当前页数 ------> " + pages.getCurrent());
        System.out.println("当前每页显示数 ------> " + pages.getSize());

        pages.getRecords().forEach(System.out :: println);
    }

自定义XML分页 

mapper

/**
 * 该接口自动实现 一些CRUD的操作
 */
public interface UserMapper extends BaseMapper<User> {
    /**
     * 如果映射的接口方法有2个参数需要@Param定义参数名,定义参数名后,映射文件中使用p.属性 c.属性,具体访 问 *
     * @param page
     * @param conditioin
     * @return
     */
    Page<User> selectUserByPage(@Param("p") Page<User> page, @Param("c") User conditioin);
}

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="com.vv.mapper.UserMapper">

    <sql id="selectSql"> SELECT* FROM user2 </sql>

    <select id="selectUserByPage" resultType="com.vv.entity.User">
        <include refid="selectSql"></include>
        <where>
            <if test="c.age !=null">
                age = #{c.age}
            </if>
            <if test="c.email !=null">
                and email like '%${c.email}%'
            </if>
        </where>
    </select>
</mapper>
/**
     * 自定xml 的分页查询
     */
    @Test
    public void page2() {
        //分页参数
        Page<User> page = new Page<>(1, 5);
        //条件对象
        User user = new User();
        user.setEmail("test4");
        user.setAge(21);

        Page<User> pages = mapper.selectUserByPage(page,user);

        System.out.println("总条数 ------> " + pages.getTotal());
        System.out.println("当前页数 ------> " + pages.getCurrent());
        System.out.println("当前每页显示数 ------> " + pages.getSize());

        pages.getRecords().forEach(System.out :: println);
    }

pageHelper分页

添加依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.11</version>
</dependency>

配置类

@Configuration
public class MybatisPlusConfig {
    /**
     * mp分页
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        // 开启 count 的 join 优化,只针对 left join
        return new PaginationInterceptor().setCountSqlParser(new JsqlParserCountOptimize(true));
    }

    /**
     * pagehelp分页插件(两个都配,不冲突)
     */
    @Bean
    public PageInterceptor pageInterceptor() {
        return new PageInterceptor();
    }
}

xml

    <select id="selectUserByPage2" resultType="com.vv.entity.User">
        <include refid="selectSql"/>
        <where>
            <if test="age!=null">
                age = #{age}
            </if>
            <if  test="email !=null">
                and email like '%${email}%'
            </if>
        </where>
    </select>

测试

/**
 * pagehelper分页
 */
@Test
public void page3() {
    //条件对象
    User user = new User();
    user.setEmail("test4");
    user.setAge(21);

    PageInfo<User> page = PageHelper.startPage(1,5).doSelectPageInfo(() ->{
        //自定义xml查询
        mapper.selectUserByPage2(user);
        //也可以使用mapper内置方法
        //mapper.selectList(Wrappers.<User>query());
    });

    List<User> list = page.getList();
    for (User user1 : list) {
        System.out.println(user1);
    }
    System.out.println("总行数=" + page.getTotal());
    System.out.println("当前页=" + page.getPageNum());
    System.out.println("每页行数=" + page.getPageSize());
    System.out.println("总页数=" + page.getPages());
    System.out.println("起始行数=" + page.getStartRow());
    System.out.println("是第一页=" + page.isIsFirstPage());
    System.out.println("是最后页=" + page.isIsLastPage());
    System.out.println("还有下一页=" + page.isHasNextPage());
    System.out.println("还有上一页=" + page.isHasPreviousPage());
    System.out.println("页码列表" + Arrays.toString(page.getNavigatepageNums()));

}

IService

 使用MyBatisPlus提供有业务层通用接口(ISerivce<T>)与业务层通用实现类(ServiceImpl<M,T> )

这样可以直接使用接口提供的方法

使用:

public interface IBookService extends IService<Book> {
}
@Service
public class BookServiceImpl extends ServiceImpl<BookMapper,Book> implements IBookService {

}

如果想自定方法可直接重载或追加

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值