MyBatis超详细入门篇

一、构建MyBatis框架

  1. 新键一个maven项目

  2. pom.xml文件中配置依赖

    示例:

    <dependencies>
    		<!--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.40</version>
            </dependency>
        </dependencies>
    
  3. resources文件夹下创建mybatis.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>
        <!--使用什么环境,default就是对应环境的id-->
        <environments default="jdbc">
            <!--jdbc连接数据库的环境-->
            <environment id="jdbc">
                <!--事务管理器-->
                <transactionManager type="JDBC"/>
                <!--数据源,type是连接池的方式进行连接-->
                <dataSource type="POOLED">
                    <!--key值是固定的-->
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost/数据库名"/>
                    <property name="username" value="用户名"/>
                    <property name="password" value="密码"/>
                </dataSource>
            </environment>
        </environments>
        <!-- 导入mapper.xml配置文件,里面配置了SQL语句和DAO接口的映射关系-->
        <mappers>
            <mapper resource="mapper.xml"/>
        </mappers>
    </configuration>
    
  4. resources文件夹下创建mapper.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">
    <!--SQL语句配置文件,需要在mybatis配置文件中进行加载-->
    
       <!--mapper标签用于做SQL语句和方法之间的映射-->
    
       <!--一个mapper对应一个类,namespace里面写的是dao接口的完整路径-->
    
       <mapper namespace="com.xzk.dao.UserDao">
    
           <!--select的id是方法名-->
    
           <!--resultType是查询的结果的一种映射,User类的属性与列名完全一致(bean类),所以查询的结果会转换成User类-->
    
           <select id="getAll" resultType="User类的完整路径:包名.类名">
    
               /具体的sql语句/
    
               select * from usertest
    
           </select>
    
       </mapper>
    
    
  5. 测试

    • bean类:

      public class User {
               private String uname;
               private String upsw;
      
               public User() {
               }
      
               public User(String uname, String upsw) {
                   this.uname = uname;
                   this.upsw = upsw;
               }
      
               public String getUname() {
                   return uname;
               }
      
               public void setUname(String uname) {
                   this.uname = uname;
               }
      
               public String getUpsw() {
                   return upsw;
               }
      
               public void setUpsw(String upsw) {
                   this.upsw = upsw;
               }
      
               @Override
               public String toString() {
                   return "User{" +
                           "uname='" + uname + '\'' +
                           ", upsw='" + upsw + '\'' +
                           '}';
               }
           }
      
    • 测试类:

      public class Test1 {
          public static void main(String[] args) throws IOException {
              //加载配置文件
              Reader reader = Resources.getResourceAsReader("mybatis.xml");
              //得到sqlSessionFactoryBuilder
              SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
              SqlSessionFactory build = builder.build(reader);
      
              //得到sqlSession
              SqlSession sqlSession = build.openSession();
              //操作sql  传入参数:类名.方法名
              List<User> list = sqlSession.selectList("com.xzk.dao.UserDao.getAll");
              //遍历
              for (User u : list){
                  System.out.println(u);
              }
      
              //关闭资源
              sqlSession.close();
              reader.close();
          }
      }
      

二‘、多数据源操作

在mapper.xml配置文件中,<environments>标签中的default属性值是某一个数据库环境的id,

在测试类中SqlSessionFactory build = builder.build(reader);此方法的第二个参数是指定的数据库环境的id,如果没有写则默认是default属性值所指的id。

三、 MyBatis中sql语句参数的传递

  1. 单个参数

    sql语句中通过#{参数名}的形式来匹配传入的参数

    例:

    mapper.xml文件中:

    <select id="findByName" parameterType="String" resultType="com.test.bean.User">
        /*指定参数的类型,以及返回结果的接收对象,sql语句中#{}中的内容与方法的参数名一致且区分大小写*/
        select * from usertest where uname = #{uname}
    </select>
    

    UserDao.java文件中:

    User findByName(String uname);
    

    测试类:

    public class Test1 {
        public static void main(String[] args) throws IOException {
            //加载配置文件
            Reader reader = Resources.getResourceAsReader("mybatis.xml");
            //得到sqlSessionFactoryBuilder
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //第二个参数是数据源的id,如果不写,则默认是mapper.xml中配置的
            SqlSessionFactory build = builder.build(reader,"jdbc2");
    
            //得到sqlSession
            SqlSession sqlSession = build.openSession();
            //操作sql  传入参数:DAO类名.方法名
            User user = sqlSession.selectOne("com.xzk.dao.UserDao.findByName", "123");
            System.out.println(user);
    
            //关闭资源
            sqlSession.close();
            reader.close();
        }
    }
    

  2. **对象作为参数传入 **:

    sql语句中通过#{属性名1}``#{属性名2}``#{属性名3}的方式来匹配传入的对象中的属性值.。值得一提的是,在进行增删改操作的时候不需要我们开启事务但是需要提交事务。

    例:

    mapper.xml文件中:

    <select id="insertUser" parameterType="com.test.bean.User">
        /*增删改操作只需要指定参数类型,不需要指定resultType属性值,默认返回受影响的数量*/
        insert into usertest(uname,upsw) values(#{uname},#{upsw})
    </select>
    

    UserDao.java文件中:

    int insertUser(User user);
    

    User.java文件中:

    public class User {
        private String uname;
        private String upsw;
        //此处省略toStrong和get、set等方法
    }
    

    测试类:

    public class Test2 {
        public static void main(String[] args) throws IOException {
            //加载配置文件
            Reader reader = Resources.getResourceAsReader("mybatis.xml");
            //得到sqlSessionFactoryBuilder
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //第二个参数是数据源的id,如果不写,则默认是mapper.xml中配置的
            SqlSessionFactory build = builder.build(reader,"jdbc2");
    
            //得到sqlSession
            SqlSession sqlSession = build.openSession();
            //操作sql  传入参数:类名.方法名
            int insert = sqlSession.insert("com.xzk.dao.UserDao.insertUser", new User("晓丽", "666"));
            sqlSession.commit();        //增删改需要提交事务!!!!!!!
            System.out.println(insert);
            //关闭资源
            sqlSession.close();
            reader.close();
        }
    }
    

  3. 多个参数值的情况

    需要将多个参数存到Map集合中,sql语句中通过#{key值}的方式来匹配传入的数据

    例:

    mapper.xml文件中:

    <select id="insertUser2" parameterType="Map">
        /*#{}中为Map中的key值*/
        insert into usertest(uname,upsw) values(#{name },#{psw})
    </select>
    

    UserDao.java文件

    int insertUser2(Map map);
    

    测试类:

    public class Test3 {
        public static void main(String[] args) throws IOException {
            //加载配置文件
            Reader reader = Resources.getResourceAsReader("mybatis.xml");
            //得到sqlSessionFactoryBuilder
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //第二个参数是数据源的id,如果不写,则默认是mapper.xml中配置的
            SqlSessionFactory build = builder.build(reader,"jdbc2");
    
            //得到sqlSession
            SqlSession sqlSession = build.openSession();
            //操作sql  传入参数:类名.方法名
            Map<String, String> map = new HashMap();
            map.put("name","小明");
            map.put("psw","123456");
            int insert = sqlSession.insert("com.xzk.dao.UserDao.insertUser2",map);
            sqlSession.commit();        //增删改需要提交事务!!!!!!!
            System.out.println(insert);
            //关闭资源
            sqlSession.close();
            reader.close();
        }
    }
    
  4. 补充:

    当没有实体类用来接收查询返回的结果时,返回的类型是map类型。

    示例:

    mapper.xml文件中:

    <select id="find" resultType="Map">
    	/*统计名字叫123的人的总数,此处若不写count则在查询的时候key值默认为count(uname)*/
        select count(uname) count from usertest where uname='123'
    </select>
    

    UserDao.java文件中

    Map find();
    

    测试类:

    public class Test4 {
        public static void main(String[] args) throws IOException {
            //加载配置文件
            Reader reader = Resources.getResourceAsReader("mybatis.xml");
            //得到sqlSessionFactoryBuilder
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //第二个参数是数据源的id,如果不写,则默认是mapper.xml中配置的
            SqlSessionFactory build = builder.build(reader,"jdbc2");
    
            //得到sqlSession
            SqlSession sqlSession = build.openSession();
            //操作sql  传入参数:类名.方法名
            Map map = sqlSession.selectOne("com.xzk.dao.UserDao.find");
            Set keySet = map.keySet();
            Iterator iterator = keySet.iterator();
            while (iterator.hasNext()){
                Object key = iterator.next();
                System.out.println(key.toString() + " = " + map.get(key));
            }
            //关闭资源
            sqlSession.close();
            reader.close();
        }
    }
    

四、通过getMapper来调取Dao的方法

不需要创建Dao接口的实现类,利用反射的原理生成代理类,然后再对Dao的方法进行调用

public class DaoTest {
    public static void main(String[] args) {
        try {
            //获得sqlSession
            Reader reader = Resources.getResourceAsReader("mybatis.xml");
            
            //工厂类
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = builder.build(reader);
            SqlSession sqlSession = build.openSession();
			
            //生成代理类来调用方法
            UserDao userDao = sqlSession.getMapper(UserDao.class);
            System.out.println(userDao);//org.apache.ibatis.binding.MapperProxy@43a25848
            userDao.getAll();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

五、ThreadLocal对sqlSession的处理

  • 首先,threadLocal并不是一个线程,他是一个线程局部变量,他会为每一个线程都创建一个单独的用于存值副本,使得每一个线程都无法访问其他线程的副本。

示例:

public class ThreadTest {
    //threadLocal对象
    private ThreadLocal<String> threadLocal = new ThreadLocal<String>();
    //用List作对比
    private List<String> list = new ArrayList<String>();

    class A extends Thread{
        @Override
        public void run() {
            System.out.println("-------------------A-----------------");
            System.out.println("A_thread_start");
            //在threadLocal进行存值
            threadLocal.set("A_threadLocal");
            list.add("A_list");
            System.out.println(threadLocal.get());
        }
    }

    class B extends Thread{
        @Override
        public void run() {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("-------------------B-----------------");
            System.out.println("B_thread_start");
            //从threadLocal进行取值
            System.out.println(threadLocal.get());
            //从list中取值
            System.out.println(list);
        }
    }

    public static void main(String[] args) {
        //创建ThreadTest对象
        ThreadTest threadTest = new ThreadTest();
        A a = threadTest.new A();
        B b = threadTest.new B();
        a.start();
        b.start();
    }

}


执行结果:

-------------------A-----------------
A_thread_start
A_threadLocal	//A线程自己可以取到自己存的值
-------------------B-----------------
B_thread_start
null	//可以看到,B线程无法访问A线程在ThreadLocal中存的值
[A_list]
  • 单例模式创建sqlSession

示例:

package com.xzk.util;

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 java.io.IOException;
import java.io.Reader;

/**
 * 单例模式
 */
public class sqlSessionUtil {
    //通过ThreadLocal来管理sqlSession
    private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();
    //工厂类
    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            Reader reader = Resources.getResourceAsReader("mybatis.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 得到sqlSession
     */
    public static SqlSession getSession(){
        SqlSession sqlSession = threadLocal.get();
        //如果threadLocal中没有session,则开启一个新的session并存到threadLocal中
        if (sqlSession == null){
            sqlSession = sqlSessionFactory.openSession();
            threadLocal.set(sqlSession);
        }
        return sqlSession;
    }

    /**
     * 关闭sqlSession
     */
    public static void closeSession(){
        SqlSession sqlSession = threadLocal.get();
        //ThreadLocal中有session
        if (sqlSession != null){
            //关闭session
            sqlSession.close();
            //从threadLocal中移除
            threadLocal.remove();
        }
    }
}

六、配置文件中类的别名(alias)

  • 方式一:给类起别名

    示例:

    mybatis.xml文件(部分代码):

    <typeAliases>
            <!--方式一:给类其别名,type的值为类的全路径-->
            <typeAlias type="com.xzk.bean.User" alias="alias"></typeAlias>
    </typeAliases>
    

    mapper.xml文件(部分代码):

    <select id="getAll" resultType="alias">
            /*具体的sql语句*/
            select * from usertest
    </select>
    

  • 方式二:给包起别名

    示例(注:此处测试的类为User类):

    mybatis.xml文件(部分代码):

    <typeAliases>
    		<!--方式二:给包期别名是,在使用包下的类时,使用小写的类名即可-->
            <package name="com.test.bean"/>
    </typeAliases>
    

    mapper.xml文件(部分代码):

    <select id="getAll" resultType="user">
            /*具体的sql语句*/
            select * from usertest
    </select>
    

七、log4j输出SQL语句

每次执行都可以看到sql语句和参数的传递

步骤:

  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. resources目录下创建log4j.properties文件,并写入以下内容

    #Debug级别 输出到Console
    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
    

控制台效果:

020-08-13 23:21:10,025 [main] DEBUG [com.test.dao.UserDao.insertUser] - ==>  Preparing: insert into usertest(uname,upsw) values(?,?) 
2020-08-13 23:21:10,051 [main] DEBUG [com.test.dao.UserDao.insertUser] - ==> Parameters: zhangsan(String), 123456(String)
2020-08-13 23:21:10,504 [main] DEBUG [com.test.dao.UserDao.insertUser] - <==    Updates: 1

八、复杂查询

  1. In查询

    在SQL语句的in后面添加foreach标签,假设:在sql语句 select * from users where uid in (1,2,3,4),1234四个球是通过list传入的,那么collection属性就是list,open属性是开始的符号,即前括号 ,separator属性是分隔符,是逗号,close是结束的符号是后括号 item是每次遍历的元素的别名,类似于Java foreach中冒号前面的对象

示例代码:

<?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.xzk.dao.UserDao2">
    <!--通过List方式传参-->
    <select id="finda" resultType="user">
        select * from usertest where id in
        <foreach collection="list" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </select>

    <!--通过数组的方式传参-->
    <select id="findb" resultType="user">
        select * from usertest where id in
        <foreach collection="array" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </select>

    <!--通过Map的方式传参-->
    <select id="findc" resultType="user">
        select * from usertest where id in
        <!--此处collection是key值-->
        <foreach collection="map_key" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </select>

</mapper>

测试类:

public class TestIn {
    public static void main(String[] args) {
        SqlSession sqlSession = sqlSessionUtil.getSession();
        UserDao2 mapper = sqlSession.getMapper(UserDao2.class);

        //1. List
        /*List list = new ArrayList();
        list.add(1);
        list.add(3);
        list.add(5);
        list.add(7);
        List<User> users = mapper.finda(list);*/

        //2. 数组
        /*int[] ids = new int[]{1,2,3,5};
        List<User> users = mapper.findb(ids);*/

        //3. Map(本质上还是传入的List集合)
        Map map = new HashMap();
        List list = new ArrayList();
        list.add(1);
        list.add(3);
        list.add(5);
        list.add(7);
        map.put("map_key",list);
        List<User> users = mapper.findc(map);

        for (User user : users) {
            System.out.println(user);
        }
        sqlSessionUtil.closeSession(sqlSession);
    }
}
  1. like模糊查询
    在SQL语句select * from users where 1=1后面添加if标签,if标签的test属性的作用是判空,例如: uid != null and uid != ’ ',标签中的内容是and uid = 2,第二个标签内的内容是and uname like ‘%’ #{uname} ‘%’ (注:#表示占位符$表示进行拼接,必出单引号不行用就换用双引号),多个参数的时候,方法的参数是用map集合的方式,用对象的方式传入参数的时候,只需要将对应的key值换成属性名即可

    示例代码(部分):

    <select id="findd" resultType="user">
            select * from usertest where 1=1
            <!--传入Map时,读取的是key值-->
            <if test="id!=null and id!=''">
                and id=#{id}
            </if>
            <if test="uname!=null and uname!=''">
                and uname like "%"#{uname}"%"
            </if>
    </select>
    
    <select id="finde" resultType="user">
            select * from usertest where 1=1
            <!--传入对象时,读取的是对象的属性-->
            <if test="upsw!=null and upsw!=''">
                and upsw like "%"#{upsw}"%"
            </if>
            <if test="uname!=null and uname!=''">
                and uname like "%"#{uname}"%"
            </if>
    </select>
    

    测试类:

    public class TestLike {
        public static void main(String[] args) {
            SqlSession session = sqlSessionUtil.getSession();
            UserDao2 mapper = session.getMapper(UserDao2.class);
    
            /*Map map = new HashMap();
            map.put("id",7);
            map.put("uname","3");
            List<User> users = mapper.findd(map);*/
    
            List<User> users = mapper.finde(new User("1", "1"));
            for (User user : users) {
                System.out.println(user);
            }
        }
    }
    

  2. btweenn and区间查询
    例如:在select 标签内的sql语句为select * from users where age btween #{begin} and #{end},传入的参数为map类型,key值分别为begin和end

示例代码(部分):

<select id="findf" resultType="user">
        <!--select * from usertest where id between #{begin} and #{end}-->
        <!--与上面的等价,需要注意的是小于号要进行特殊处理,防止特殊符号进行编译-->
        select * from usertest where id > #{begin} and id <![CDATA[<=]]> #{end}
</select>

测试类:

public class TestBetween {
    public static void main(String[] args) {
        SqlSession session = sqlSessionUtil.getSession();
        UserDao2 mapper = session.getMapper(UserDao2.class);
        Map map = new HashMap();
        map.put("begin",5);
        map.put("end",10);
        List<User> users = mapper.findf(map);

        for (User user : users) {
            System.out.println(user);
        }
    }
}

九、ResultMap结果映射

处理属性与列名不一致的问题,对其进行映射

示例代码:

1.原先的代码

<select id="findf" resultMap="com.xzk.bean.User">
        <!--select * from usertest where id between #{begin} and #{end}-->
        <!--与上面的等价,需要注意的是小于号要进行特殊处理,防止特殊符号进行编译-->
        select * from usertest where id > #{begin} and id <![CDATA[<=]]> #{end}

</select>

2.使用resultMap后的代码

<resultMap id="rs1" type="com.xzk.bean.User">
        <!--此处只需要写列明和对应的属性名不一致的-->
        <result property="username" column="uname"></result>
</resultMap>

<select id="findf" resultMap="rs1">
        <!--select * from usertest where id between #{begin} and #{end}-->
        <!--与上面的等价,需要注意的是小于号要进行特殊处理,防止特殊符号进行编译-->
        select * from usertest where id > #{begin} and id <![CDATA[<=]]> #{end}
</select>

十、多表查询

一对多时,一的一方集合属性用collection标签(多对一时是association标签),其中ofType属性是类的全路径(多对一时是javaType属性),在collection标签内添加result子标签来描述集合存储的类的属性和列名的映射关系

小结:先描述自身属性,再描述关联属性。一方有多方的一个集合,多方有一方的一个对象

  1. 一对多**

    示例代码:

    <!--一对多-->
    <resultMap id="rs1" type="com.xzk.bean.Grade">
            <!--先描述自身信息,之后再描述关联表的信息-->
            <!--主键用id描述,非主键用result描述-->
            <id property="gid" column="id"></id>
            <result property="gname" column="gname"></result>
            <collection property="students" ofType="com.xzk.bean.Student">
                <id property="studentid" column="studentid"></id>
                <result property="studentno" column="studentno"></result>
                <result property="studentname" column="studentname"></result>
                <result property="studentage" column="studentage"></result>
                <result property="gradeid" column="gradeid"></result>
            </collection>
    </resultMap>
    
    <!--两表联查,返回结果用resultMap-->
    <select id="findByGid" resultMap="rs1">
            select * from grade_test as g left join student_test as s on g.id = s.gradeid where g.id = #{id}
    </select>
    

    测试代码:

    public class TestGrade {
        public static void main(String[] args) {
            SqlSession session = sqlSessionUtil.getSession();
            GradeDao mapper = session.getMapper(GradeDao.class);
    
            Grade grade = mapper.findByGid(1);
            List<Student> students = grade.getStudents();
    
            for (Student student : students) {
                System.out.print(student);
                System.out.printf(student.getGrade().getGname());
                System.out.println();
            }
        }
    }
    

  2. 多对一

    示例代码:

    <!--多对一-->
        <resultMap id="rs2" type="com.xzk.bean.Student">
            <id property="studentid" column="studentid"></id>
            <result property="studentno" column="studentno"></result>
            <result property="studentname" column="studentname"></result>
            <result property="studentage" column="studentage"></result>
            <result property="gradeid" column="gradeid"></result>
            <association property="grade" javaType="com.xzk.bean.Grade">
                <id property="gid" column="gid"></id>
                <result property="gname" column="gname"></result>
            </association>
        </resultMap>
    
        <select id="findAllStudent" resultMap="rs2">
            select * from grade_test as g left join student_test as s on g.id = s.gradeid
        </select>
    

    测试代码:

    public class TestGrade {
        public static void main(String[] args) {
            SqlSession session = sqlSessionUtil.getSession();
            GradeDao mapper = session.getMapper(GradeDao.class);
    
            List<Student> students = mapper.findAllStudent();
    
            for (Student student : students) {
                System.out.print(student);
                System.out.printf(student.getGrade().getGname());
                System.out.println();
            }
        }
    }
    

十一、分页查询

分页查询有两个方式:

  • 方式一:内存分页

    代码示例:

    分页前:

    public class Test1 {
        public static void main(String[] args) throws IOException {
            //加载配置文件
            Reader reader = Resources.getResourceAsReader("mybatis.xml");
            //得到sqlSessionFactoryBuilder
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //第二个参数是数据源的id,如果不写,则默认是mapper.xml中配置的
            SqlSessionFactory build = builder.build(reader,"jdbc2");
    
            //得到sqlSession
            SqlSession sqlSession = build.openSession();
            //操作sql  传入参数:类名.方法名
            List<User> list = sqlSession.selectList("com.xzk.dao.UserDao.getAll");
            //遍历
            for (User u : list){
                System.out.println(u);
            }
    
            //关闭资源
            sqlSession.close();
            reader.close();
        }
    }
    

    分页后:

    public class Test1 {
        public static void main(String[] args) throws IOException {
            //加载配置文件
            Reader reader = Resources.getResourceAsReader("mybatis.xml");
            //得到sqlSessionFactoryBuilder
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //第二个参数是数据源的id,如果不写,则默认是mapper.xml中配置的
            SqlSessionFactory build = builder.build(reader,"jdbc2");
    
            //得到sqlSession
            SqlSession sqlSession = build.openSession();
            //操作sql  传入参数:类名.方法名
            List<User> list = sqlSession.selectList("com.xzk.dao.UserDao.getAll",null,new RowBounds(0,3));
            //遍历
            for (User u : list){
                System.out.println(u);
            }
    
            //关闭资源
            sqlSession.close();
            reader.close();
        }
    }
    

    对比:

    经过对比我们发现,唯一的区别只是在sqlSession.selectList方法内多了个RowBounds参数,接下来我们看看RowBounds的构造函数

    public RowBounds(int offset, int limit) {
            this.offset = offset;
            this.limit = limit;
    }
    

    可以看到,第一个参数是偏移量,即从那个位置开始,第二个参数表示显示的数量

  • 方式二:Mybatis通过插件进行物理分页

    步骤:

    1. 添加依赖

      <dependency>
      	<groupId>com.github.pagehelper</groupId>
      	<artifactId>pagehelper</artifactId>
      	<version>5.1.6</version>
      </dependency>
      
    2. 添加mybatis.xml配置文件中添加插件

      <plugins>
      	<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
      </plugins>
      
    3. 测试代码中(重点关注sql操作的步骤):

      public class Test_PageTest {
          public static void main(String[] args) throws IOException {
              //加载配置文件
              Reader reader = Resources.getResourceAsReader("mybatis.xml");
              //得到sqlSessionFactoryBuilder
              SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
              //第二个参数是数据源的id,如果不写,则默认是mapper.xml中配置的
              SqlSessionFactory build = builder.build(reader,"jdbc2");
      
              //得到sqlSession
              SqlSession sqlSession = build.openSession();
              //操作sql
                  //先指定分页的参数
              PageHelper.startPage(2,3);  //开始显示第一页,每页显示3个
                  //调取dao层方法
              List<User> list = sqlSession.selectList("com.xzk.dao.UserDao.getAll");
                  //从分页数据中获得数据
              PageInfo<User> info = new PageInfo<User>(list);
                  //遍历
              for (User user : info.getList()){
                  System.out.println(user);
              }
      
              //关闭资源
              sqlSession.close();
              reader.close();
          }
      }
      
    4. 执行的时候sql语句是怎样的?

      Preparing: select * from usertest LIMIT ?, ? 
      

      这个插件会在sql语句的后面自动添加limit来进行分页操作

十二、Mybatis缓存

mybatis缓存分为两个级别:

  • sqlSession级别(一级缓存,自动开启)

    当一个sqlSession中连续两次进行相同的sql操作时,第二次不会进行数据库访问,而是会从缓存中获取数据,并且不同的sqlSession是不能互相访问数据的

    例(部分代码):

    //操作sql  传入参数:类名.方法名
    System.out.println("--------------first-----------");
    List<User> list = sqlSession.selectList("com.xzk.dao.UserDao.getAll");
    System.out.println("--------------second-----------");
    sqlSession.selectList("com.xzk.dao.UserDao.getAll");
    

    控制台结果:

    --------------first-----------
    Sun Aug 16 22:15:05 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
    2020-08-16 22:15:06,586 [main] DEBUG [com.xzk.dao.UserDao.getAll] - ==>  Preparing: select * from usertest 
    2020-08-16 22:15:06,608 [main] DEBUG [com.xzk.dao.UserDao.getAll] - ==> Parameters: 
    2020-08-16 22:15:06,781 [main] DEBUG [com.xzk.dao.UserDao.getAll] - <==      Total: 32
    --------------second-----------
    

    总结:可以很清晰的看到,通过log4j日志,我们知道,第二次是没有进行数据库访问的。因此没有日志信息输出

  • sqlSessionFactory级别(二级缓存)

    二级缓存使得Factiry创建的不同sqlSession都能够访问缓存。因为是在mapper为文件中配置的,因此在其他mapper文件中就无法访问此二级缓存

    • 开启缓存:

      在mapper文件中,配置如下信息,来开启并配置二级缓存

      <!--开启二级缓存-->
          <cache eviction="FIFO"
                  flushInterval="60000"
                  size="512"
                  readOnly="true"/>
      

      四个属性分别代表:缓存策略、缓存刷新间隔、缓存对象的个数和只读

    • 测试代码:

      public class Test1 {
          public static void main(String[] args) throws IOException {
              //加载配置文件
              Reader reader = Resources.getResourceAsReader("mybatis.xml");
              //得到sqlSessionFactoryBuilder
              SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
              //第二个参数是数据源的id,如果不写,则默认是mapper.xml中配置的
              SqlSessionFactory build = builder.build(reader,"jdbc2");
              //得到sqlSession
              SqlSession sqlSession1 = build.openSession();
      
              //操作sql  传入参数:类名.方法名
              System.out.println("--------------first-----------");
              sqlSession1.selectList("com.xzk.dao.UserDao.getAll");
              sqlSession1.close();
              System.out.println("---------------sqlSession1_closed--------------");
              System.out.println("--------------second-----------");
              SqlSession sqlSession2 = build.openSession();
              sqlSession2.selectList("com.xzk.dao.UserDao.getAll");
              sqlSession2.close();
              System.out.println("---------------sqlSession2_closed--------------");
      
              //关闭资源
              reader.close();
          }
      }
      

      控制台输出的结果:

      --------------first-----------
      2020-08-16 22:33:11,681 [main] DEBUG [com.xzk.dao.UserDao] - Cache Hit Ratio [com.xzk.dao.UserDao]: 0.0
      Sun Aug 16 22:33:12 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
      2020-08-16 22:33:13,637 [main] DEBUG [com.xzk.dao.UserDao.getAll] - ==>  Preparing: select * from usertest 
      2020-08-16 22:33:13,659 [main] DEBUG [com.xzk.dao.UserDao.getAll] - ==> Parameters: 
      2020-08-16 22:33:13,929 [main] DEBUG [com.xzk.dao.UserDao.getAll] - <==      Total: 32
      ---------------sqlSession1_closed--------------
      --------------second-----------
      2020-08-16 22:33:14,092 [main] DEBUG [com.xzk.dao.UserDao] - Cache Hit Ratio [com.xzk.dao.UserDao]: 0.5
      ---------------sqlSession2_closed--------------
      

      总结:通过日志信息我们可以看到,不同的sqlSession是可以访问同一个二级缓存的

十三、Mybatis注解

​ 常见的insert select delete update注释,里面写的是sql语句,写在dao接口类的上面,作用与mapper文件里的select等标签类似,用于sql语句和接口方法做配对,results注释功能与resultMap标签的功能类似,用于查询的列名与属性名做配对。
为了当时sql语句被重复的写,存在一个insertProvider selectProvider deleteProvider updateProvider注释,用法,首先将sql语句统一写在一个类的方法中,一个sql语句对应一个方法,方法返回值必须去String因为,返回的是sql语句,然后在xxxProvider标签内配置sql语句所在的类的class和方法名即可,这种思想类似于aop对于两表联查,使用resultMap注解和mapper配置文件都使用的方法,首先在mapper配置文件中的ResultMap标签内配置好结果的映射关系并设置id属性,然后在resultMap注解内写上 dao类的全路径.对应resultMap标签的id属性 即可

  • 常用的增删改查注解

    例:

    public interface UserDao {
        //增
        @Insert("insert into usertest(uname,upsw) values(#{uname},#{upsw})")
        @Options(useGeneratedKeys = true,keyProperty = "id")//把自增的id对应到id属性上
        int insertUser(User user);
    
    //删
    @Delete("delete from usertest where id=#{id}")
    int deleteUserById(int id);
    
    
    //改
    @Update("update usertest set uname=#{uname},upsw=#{upsw} where id=#{id}")
    int updateUser(User user);
    
    
    //查
    @Select("select * from usertest")
    List<User> findAll();
    @Select("select count(*) from usertest")
    int totalCount();
    

    }

    
    
  • xxxProvider注解

    该注解的作用在于,在写sqp语句的时候,统一写到一个类中,然后需要的时候,使用xxxProvider注解进行调用即可,不需要每次都在接口抽象方法上写一遍

    例:

    统一写sql语句的类:

    public class sqlUtil {
        //调用xxxProvider注解其实是调用某个类的方法来得到sql语句,因此返回值必须是String类型的sql语句
    
        //增
        public String insertMethod(){
            return "insert into usertest(uname,upsw) values(#{uname},#{upsw})";
        }
        //删
        public String deleteMethod(){
            return "delete from usertest where id=#{id}";
        }
        //改
        public String updateMethod(){
            return "update usertest set uname=#{uname},upsw=#{upsw} where id=#{id}";
        }
        //查
        public String selectAllMethod(){
            return "select * from usertest";
        }
    
        public String countMethod(){
            return "select count(*) from usertest";
        }
    }
    

    UserDao类中:

    public interface UserDao {
        //增
        @InsertProvider(type = sqlUtil.class,method = "insertMethod")
        @Options(useGeneratedKeys = true,keyProperty = "id")//把自增的id对应到id属性上
        int insertUser(User user);
    
    
        //删
        @DeleteProvider(type = sqlUtil.class,method = "deleteMethod")
        int deleteUserById(int id);
    
    
        //改
        @UpdateProvider(type = sqlUtil.class,method = "updateMethod")
        int updateUser(User user);
    
    
        //查
        @SelectProvider(type = sqlUtil.class,method = "selectAllMethod")
        List<User> findAll();
        @SelectProvider(type = sqlUtil.class,method = "countMethod")
        int totalCount();
    }
    
  • ResultMap注解,两表联查

    和前面学的ResultMap差不多,首先,现在mapper文件中写好resultMap并给一个id名,然后通过注解调用,注解中的写法是:@ResultMap(“namespace.id”)

    namespace是mapper标签的属性,id是resultMap标签的属性,通过这两个可以定位到具体的映射关系,下面我们通过示例代码来看一下

    mapper文件中:

    <?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.xzk.dao.UserDao">
        <!--一对多-->
        <resultMap id="rs1" type="com.xzk.bean.Grade">
            <!--先描述自身信息,之后再描述关联表的信息-->
            <!--主键用id描述,非主键用result描述-->
            <id property="gid" column="id"></id>
            <result property="gname" column="gname"></result>
            <collection property="students" ofType="com.xzk.bean.Student">
                <id property="studentid" column="studentid"></id>
                <result property="studentno" column="studentno"></result>
                <result property="studentname" column="studentname"></result>
                <result property="studentage" column="studentage"></result>
                <result property="gradeid" column="gradeid"></result>
            </collection>
        </resultMap>
    </mapper>
    

    接口中(部分代码):

    //两表联查
        @ResultMap("com.xzk.dao.UserDao.rs1")
        @Select("select * from student_test s left join grade_test g on s.gradeid = g.id where g.id = #{id}")
        public Grade selectAll( int id);
    
  • Param注解,参数绑定

    次注解用于参数前面,用于建立参数和sql语句参数之间的对应关系

    示例代码:

     @Update("update usertest set uname=#{uname},upsw=#{upsw} where id=#{id}")
    int updateUser2(@Param("uname") String name,@Param("upsw") String password,@Param("id") int uid);
    
    
  • 使用注解开启二级缓存

    1. 在对应的方法上添加如下注解

      @Option(useCache = true
      		flushCache = Options.FlushCachePolicy.FALSE,//查询时不刷新缓存
      		timeout = 10000)	//查询结果缓存10000秒
      
    2. 在dao接口类上添加如下注解

      @CacheNamespace(size=512)	//缓存512个对象
      @CacheNamespace //默认缓存1024个对象
      

十四、lombok插件

lombok的使用

@Data 注解在类上;提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual、hashCode、

toString 方法

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

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

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

@NoArgsConstructor :注解在类上;为类提供一个无参的构造方法

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

@Cleanup : 可以关闭流

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

@Synchronized : 加个同步锁

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

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

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

@ToString 重写toString()方法

十五、实体类自动生成

步骤:

  1. 添加依赖和插件

    在pom.xml文件中添加如下内容

    <dependencies>
            <dependency>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-core</artifactId>
                <version>1.3.5</version>
            </dependency>
        </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>
    
  2. 新键generatorConfig.xml文件

    从上面的代码的<configurationFile>标签内可以看到,需要一个generatorConfig.xml文件,新键并添加如下信息,根据自己的情况进行修改,需要修改的内容如下:

    • 驱动包地址
    • 连接数据库的信息
    • 实体类生成的路径
    • mapper文件生成的路径
    • dao文件生成的路径
    • 最后table标签内tableName属性写上要生成的实体类对应的表名
    <?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 -->
        <!--本地仓库驱动包的地址 -->
        <classPathEntry location="F:\maven\maven_repository\mysql\mysql-connector-java\5.1.46\mysql-connector-java-5.1.46.jar"/>
        <context id="MyBatis" targetRuntime="MyBatis3">
        <!--去除注释 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <!--数据库连接 -->
        <jdbcConnection
                driverClass="com.mysql.jdbc.Driver"
                connectionURL="jdbc:mysql://localhost:3306/vernhe"
                userId="root"
                password="123456">
        </jdbcConnection>
        <!--生成实体类 指定包名 以及生成的地址 (可以自定义地址,但是路径不存在不会自动创建 使用Maven生成在target目录下,会自动创建) -->
        <javaModelGenerator
                targetPackage="com.yhp.bean"
                targetProject="F:\JavaProject\autoTest\src\main\java">
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <!--生成SQLmapper文件 -->
        <sqlMapGenerator
                targetPackage="mapper"
                targetProject="F:\JavaProject\autoTest\src\main\resources">
        </sqlMapGenerator>
        <!--生成Dao文件,生成接口 -->
        <javaClientGenerator
                type="XMLMAPPER"
                targetPackage="com.yhp.dao"
                targetProject="F:\JavaProject\autoTest\src\main\java">
        </javaClientGenerator>
        <table tableName="student_test" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false"
               enableSelectByExample="false" selectByExampleQueryId="false">
        </table>
        <table tableName="grade_test" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false"
               enableSelectByExample="false" selectByExampleQueryId="false">
        </table>
    </context>
    </generatorConfiguration>
    

  • 15
    点赞
  • 100
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

生命中有太多不确定

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值