MyBatis

1.概述

1.1.什么是框架

框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用方面而后者是从目的方面给出的定义。

简而言之,框架其实就是某种应用的半成品,就是一组组件,供你选用完成你自己的系统,或者说是使用别人搭好的舞台,你来做表演。框架一般是成熟的,不断升级的软件。

1.2.框架要解决的问题

框架要解决的最重要的一个问题是技术整合的问题,在JavaEE的 框架中,有着各种各样的技术,不同的软件企业需要从JavaEE中选择不同的技术,这就使得软件企业最终的应用依赖于这些技术,技术自身的复杂性和技术的风险性将会直接对应用造成冲击。而应用是软件企业的核心,是竞争力的关键所在,因此应该将应用自身的设计和具体的实现技术解耦。这样,软件企业的研发将集中在应用的设计上,而不是具体的技术实现,技术实现是应用的底层支撑,它不应该直接对应用产生影响。

1.3.软件开发的分层重要性

框架的重要性在于它实现了部分功能,并且能够很好的将低层应用平台和高层业务逻辑进行了缓和。为了实现软件工程中的“高内聚、低耦合”。把问题划分开来各个解决,易于控制,易于延展,易于分配资源。我们常见的MVC软件设计思想就是很好的分层思想。

通过分层更好的实现了各个部分的职责,在每一层将再细化出不同的框架,分别解决各层关注的问题。
在这里插入图片描述

1.4.分层开发下的常见框架

1.4.1 解决数据持久化问题的框架

Mybatis

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

作为持久层的框架,还有一个封装程度更高的框架就是Hibernate,但这个框架因为各种原因目前在国内的流行程度下降太多,现在公司开发也越来越少使用。目前Mybatis框架是主流,未来使用Spring Data来实现数据持久化也是一种趋势。

1.4.2 解决WEB层问题的框架

SpringMVC

Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或集成其他MVC开发框架,如Struts1(现在一般不用),Struts2(一般老项目使用)等。

1.4.3 解决技术整合问题的框架

Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。

  • 目的:解决企业应用开发的复杂性
  • 功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
  • 范围:任何Java应用
    Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。

1.5 Mybatis框架概述

Mybatis是一个优秀的基于Java的持久层框架,它内部封装了Jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。

Mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由Mybatis框架执行sql并将结果映射为java对象并返回。

采用ORM思想解决了实体和数据库映射的问题,对Jdbc进行了封装,屏蔽了Jdbc Api底层访问细节,使我们不用与Jdbc Api打交道,就可以完成对数据库的持久化操作。

ORM :操作对象既是操作数据库

为了我们能够更好掌握框架运行的内部过程,并且有更好的体验,下面我们将从自定义Mybatis框架开始来学习框架。此时我们将会体验框架从无到有的过程体验,也能够很好的综合前面阶段所学的基础。

2 .Mybatis 入门程序

案例场景: 根据 user 表,查询出所有“上海”的用户对象,返回一个pojo的 List集合。

2.1 初始化测试数据

1、创建数据库,数据库名:mybatisDemo

2、将如下Sql语句在数据库中执行,初始化测试数据

-- ----------------------------

-- Table structure for `user`

-- ----------------------------

CREATE DATABASE mybatisdemo;

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `username` varchar(32) NOT NULL COMMENT '用户名称',

  `birthday` date DEFAULT NULL COMMENT '生日',

  `sex` char(1) DEFAULT NULL COMMENT '性别',

  `address` varchar(256) DEFAULT NULL COMMENT '地址',

  PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;

 

-- ----------------------------

-- Records of user

-- ----------------------------

INSERT INTO `user` VALUES ('1', '张三', '2018-07-10', '1', '北京');

INSERT INTO `user` VALUES ('2', '李四', '2018-07-10', '1', '上海');

INSERT INTO `user` VALUES ('3', '王五', '2018-07-10', '1', '广州');

INSERT INTO `user` VALUES ('4', '王六', '2018-07-10', '1', '深圳');

INSERT INTO `user` VALUES ('5', '王八', '2018-07-10', '1', '上海');

2.2 JDBC 实现

代码:
创建Pojo 类

import java.util.Date;
public class User {
    private Integer id;
    private String username;
    private String sex;
    private Date birthday;
    private String address;
 
    // 省略getter ,setter,toString 方法
     // ......
       // ......
}

JDBC问题分析:

上边使用JDBC的原始方法(未经封装)实现了查询数据库表记录的操作。

  1. 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。

  2. Sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变Java代码。

  3. 使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。

  4. 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。

下面我们将通过Mybatis 解决部分问题。

2.3 Mybatis 实现

步骤1-a: 创建Maven 工程,引入 Mybatis jar 包。(普通工程)

在pom 文件中加入一下代码

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.3</version>
</dependency>

配置jdk编译环境

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                    </configuration>
        </plugin>
    </plugins>
</build>

步骤1-b 创建Spring boot 工程, 引入以下依赖.(Spring Boot)

<dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.1.1</version>
    </dependency>

步骤2: 定义Dao接口和实现类

Dao接口
public interface UserDao {
    User selectUserById(int id);
    public List<User> selectUserByAddr(String address);

public class UserDaoImpl implements UserDao{
    public User selectUserById(int id){
        return null;
    }
    public List<User>selectUserByAddr(String address){
        return null;
    }  
}
}

步骤3: 新建 mybatis-config.xml 文件(Spring Boot 请略过)
在resources 文件夹中新建一个 mybatis-config.xml 文件, 主要包含数据库连接信息

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="env1">
        <environment id="env1">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatisdemo"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
 
<!--
    <mappers>
        <mapper resource="mapper/UserDao.xml"/>
    </mappers>
-->
</configuration>

步骤4: 创建映射文件

在resources 文件夹中新建一个 UserDao.xml 文件,主要是包含 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">
<mapper namespace="net.suncaper.mybatisdemo.dao.UserDao">
    <select id="selectUser1" parameterType="java.lang.String" resultType="net.suncaper.mybatisdemo.entity.User">
        select  id, username ,birthday,sex , address from user where address  = #{address}
    </select>
    <select id="selectUser2" parameterType="_int" resultType="net.suncaper.mybatisdemo.entity.User">
        select  id, username ,birthday,sex , address from user where id = #{id}
    </select>
</mapper>

注意: 需要把映射文件添加到配置文件中

步骤5: 实现查询User对象
UserDaoImpl.java

public class UserDaoImpl implements UserDao {
    private SqlSession sqlSession;
    //构造注入
    public UserDaoImpl(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }
 
    @Override
    public User selectUserById(int id) {
        return sqlSession.selectOne("s1",id);
    }
 
    @Override
    public List<User> selectUserByAddr(String address) {
        return sqlSession.selectList("s2",address);
    }
}

Test运行测试代码

package demo2test;
 
import net.suncaper.dao.UserDao;
import net.suncaper.dao.UserDaoImpl;
import net.suncaper.entity.User;
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.Before;
import org.junit.Test;
 
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
 
public class MybatisTest {
    SqlSession sqlSession = null;
 
    @Before
    public void before() {
        try {
            //1. 读取配置文件,(已经包含映射文件)
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//            2.  创建SqlSessionFactory
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//        3. 打开 sqlSession
            sqlSession = sessionFactory.openSession();
        } catch (IOException e) {
            e.printStackTrace();
        }
 
    }
 
    //想办法运行 映射文件的sql 语句
    @Test
    public void test0() {
        User user = sqlSession.selectOne("s1", 1);
        System.out.println(user);
        System.out.println("==============================================================");
        List<User> users = sqlSession.selectList("s2", "上海");
        for (User u :
                users) {
            System.out.println(u);
        }
    }
 
 
    @Test
    public void test1() {
        UserDao userDao = new UserDaoImpl(sqlSession);
        System.out.println( userDao.selectUserById(1) );
    }
 
    @Test
    public void test2() {
        UserDao userDao = new UserDaoImpl(sqlSession);
        List<User> users = userDao.selectUserByAddr("上海");
        for (User u :
                users) {
            System.out.println(u);
        }
    }
 
}

==步骤6: 映射文件中添加 resultMap ==

<?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="net.suncaper.dao.UserDao">
    <resultMap id="baseResultMap" type="net.suncaper.entity.User">
        <id column="id" property="id"></id>
        <result  column="username" property="name"/>
        <result column="birthday" property="birthday"/>
        <result column="sex" property="sex"/>
        <result column="address" property="address"/>
    </resultMap>
    <select id="s1" parameterType="_int" resultMap="baseResultMap">
       select  id, username ,birthday,sex , address from user where id  = #{value}
    </select>
 
    <select id="s2" parameterType="string" resultMap="baseResultMap">
       select  id, username  ,birthday,sex , address from user where address  = #{address}
    </select>
</mapper>

步骤7: 改成用Mapper的方式来实现

  1. 写dao接口,习惯性用xxxMapper

写dao接口,习惯性用xxxMapper

public interface UserMapper {
    User selectUserById(int id);
    List<User> selectUserByAddr(String address);
}
  1. 保证映射文件中 sql 语句的id名和 mapper 接口中的方法名保持一致
    mapper.xml 映射文件
<select id="selectUserById" parameterType="_int" resultMap="baseResultMap">
      select  id, username ,birthday,sex , address from user where id  = #{value}
</select>
<select id="s2" parameterType="string" resultMap="baseResultMap">
      select  id, username  ,birthday,sex , address from user where address  = #{address}
</select>
  1. 直接使用 Mapper接口的代理类,(直接使用 Dao接口,Mybatis自动生产代理的实现类)
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectUserByAddr("上海");

2.4 Mybatis 使用 insert|update|delete|select 完成 CRUD

select:

  • Mapper 接口方法
public Employee getEmployeeById(Integer id );
  • Mapper 映射文件
<select id="getEmployeeById" resultType="net.suncaper.mybatis.beans.Employee">
    select * from tbl_employee where id =${_parameter}
</select>

insert:

  • Mapper 接口方法
public Integer insertEmployee(Employee employee);
  • Mapper 映射文件
<insert id="insertEmployee"  parameterType="com.mybatis.beans.Employee">
    insert into tbl_employee(last_name,email,gender) values(#{lastName},#{email},#{gender})
</insert>

update:

  • Mapper 接口方法
public Boolean updateEmployee(Employee employee);
  • Mapper 映射文件
<update id="updateEmployee" >
    update tbl_employee set last_name = #{lastName},email = #{email},gender = #{gender} where id = #{id}
</update>

delete:

  • Mapper 接口方法
public void deleteEmployeeById(Integer id );
  • Mapper 映射文件
<delete id="deleteEmployeeById" >
    delete from tbl_employee where id= #{id}
</delete>

3.Mybatis案例

3.1(查询)

案例1:两个以上sql 参数

数据库:

drop database if exists mybatisdemo;
create  database mybatisdemo;
use mybatisdemo;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id`         int(11) NOT NULL AUTO_INCREMENT,
  `username`  varchar(32) NOT NULL COMMENT '用户名称',
  `birthday`  date DEFAULT NULL COMMENT '生日',
  `sex`       char(1) DEFAULT NULL COMMENT '性别',
  `address`   varchar(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
INSERT INTO `user` VALUES ('1', '张三', '2018-07-10', '1', '北京');
INSERT INTO `user` VALUES ('2', '李四', '2018-07-10', '0', '上海');
INSERT INTO `user` VALUES ('3', '王五', '2018-07-10', '1', '广州');
INSERT INTO `user` VALUES ('4', '王六', '2018-07-10', '1', '深圳');
INSERT INTO `user` VALUES ('5', '王八', '2018-07-10', '1', '上海');
 
create table employee(
    id varchar(255) primary key,
    name varchar(255)
)

场景: 查在上海的女人

select * from user whrere sex = ? and address = ?
  1. 通过Map 的方式
  • Mapper 接口方法
List<User> selectUserByAddrAndSexA(Map<String,Object> map);
  • Mapper 映射文件
<select id="selectUserByAddrAndSexA" parameterType="map" resultMap="userMap">
        select * from user where sex = #{sex} and address = #{address}
 </select>
  • 测试代码
Map<String,Object> map = new HashMap<>();
map.put("sex",0);
map.put("address","上海");
List<User> userList = userMapper.selectUserByAddrAndSexA(map);
  1. 通过java Bean 的方式
  • Mapper 接口方法
List<User> selectUserByAddrAndSexB(User user);
  • Mapper 映射文件
<select id="selectUserByAddrAndSexB" parameterType="net.suncaper.mybatisdemo.domain.User" resultMap="userMap">
        select * from user where sex = #{sex} and address = #{address}
 </select>
  • 测试代码
User user = new User();
user.setSex("0");
user.setAddress("上海");
List<User> userList = userMapper.selectUserByAddrAndSexB(user);
  1. 通过java Bean + 命名式参数 的方式
  • Mapper 接口方法
List<User> selectUserByAddrAndSexC(@Param("u") User user);
  • Mapper 映射文件
<select id="selectUserByAddrAndSexC" resultMap="userMap">
        select * from user where sex = #{u.sex} and address = #{u.address}
 </select>
  • 测试代码
User user = new User();
user.setSex("0");
user.setAddress("上海");
List<User> userList = userMapper.selectUserByAddrAndSexC(user);
  1. 通过命名式参数 的方式
  • Mapper 接口方法
List<User> selectUserByAddrAndSexD(@Param("a") String sex,@Param("b")String address);
  • Mapper 映射文件
<select id="selectUserByAddrAndSexD" resultMap="userMap">
        select * from user where sex = #{a} and address = #{b}
 </select>
  • 测试代码
List<User> userList = userMapper.selectUserByAddrAndSexD("0","上海");

案例2:以集合或数组作为参数

查询广州、北京、上海、深圳的人

  • Mapper 接口方法
List<User> selectUserBySomeAddr(@Param("addresses") String[] addresses);

  • Mapper 映射文件
<select id="selectUserBySomeAddr"  resultMap="userMap">
       select * from user where address in
       <foreach collection="addresses" open="(" close=")" separator="," item="addr">
         #{addr}
       </foreach>
 </select>
  • 测试代码
List<User> userList= userMapper.selectUserBySomeAddr(new String[]{"上海","广州","深圳","北京"});

案例3:演示# 和 $ 的区别

  • $ 只是字符串的拼接,不能防止SQL注入
  • ${value} 会被直接替换,而 #{value} 会被使用 ?作为 预处理
  • Mapper 接口方法
List<User>  selectUserByName(String name);

  • Mapper 映射文件
<select id="selectUserByName" parameterType="string" resultMap="userMap">
         select * from user where username = '${value }'
 </select>
  • 测试代码
List<User> users = userMapper.selectUserByName("王八");

案例4:模糊查询

场景:查询名字 包含”王“ 字混球

方式1:通过$符号
  • Mapper 接口方法
List<User>  selectUserLikeName(String name);
  • Mapper 映射文件
<select id="selectUserLikeName" parameterType="string" resultMap="userMap">
       select * from user where username like '%${value}%'
   </select>
  • 测试代码
List<User> users = userMapper.selectUserLikeName("王");
方式2:通过字符串连接函数
  • Mapper 接口方法
List<User>  selectUserLikeName1(String name);
  • Mapper 映射文件
<select id="selectUserLikeName1" parameterType="string" resultMap="userMap">
       select * from user where username like CONCAT('%',#{value},'%')
    </select>
  • 测试代码
List<User> users = userMapper.selectUserLikeName1("王");
方式3:通过 标签
  • Mapper 接口方法
List<User>  selectUserLikeName2(@Param("name")String name);
  • Mapper 映射文件
<select id="selectUserLikeName2" parameterType="string" resultMap="userMap">
      <bind name="item" value="'%'+name+'%'"/>
       select * from user where username like  #{item}
  </select>
  • 测试代码
List<User> users = userMapper.selectUserLikeName2("王");

案例5:插入语句并获得主键

方式1: 通过useGeneratedKeys

通过 useGeneratedKeys拿到数据库自动增长的id值,赋给插入对象的主键字段(user 对象的id属性)

  • Mapper 接口方法
void insertUser(User user);
  • Mapper 映射文件
<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex}, #{address} )
 </insert>
  • 测试代码
User user = new User("龙哥","0",new Date(),"重庆");
  userMapper.insertUser(user);
        //持有持久化ID
  System.out.println(user.getId());
方式2: 通过数据库函数实现

自动增长的id : select last_insert_id()

获取uuid: select uuid()

  • Mapper 接口方法
void insertEmployee(Employee employee);
void insertUser1(User user);
  • Mapper 映射文件
<!-- 字符串类型作为主键 -->
<insert id="insertEmployee" parameterType="Employee">
        <selectKey resultType="string" keyProperty="id" order="BEFORE">
            select uuid()
        </selectKey>
        insert into employee(id,name) values(#{id},#{name})
    </insert>
 
<!-- int 类型作为主键 -->
<insert id="insertUser1" parameterType="User">
        <selectKey resultType="_int" keyProperty="id" order="AFTER">
            select last_insert_id()
        </selectKey>
        insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex}, #{address})
 </insert>
  • 测试代码
Employee employee = new Employee();
     /*  employee.setId(IDGener.getUUID()); 默认情况下,缺少主键不能插入数据库*/
 employee.setName("员工222");
 employeeMapper.insertEmployee(employee);
 System.out.println(employee.getId());
//-------------------------------
 User user = new User("黄爷","0",new Date(),"重庆");
 userMapper.insertUser1(user);       
 System.out.println(user.getId());

案例6:更新和删除操作

通过删除和更新,事务处理

默认情况下 sqlSession 是事务不自动提交,需要显式提交

  • Mapper 接口方法
public void deleteUserByName(User user);
void updateUserNameById(User user);
  • Mapper 映射文件
<delete id="deleteUserByName" parameterType="User">
      delete  from user where username = #{username}
</delete>
<update id="updateUserNameById" parameterType="User">
      update  user set username = #{username} where id = #{id}
 </update>
  • 测试代码
@Test
   public void testDeleteUserByName(){
       boolean isAuto = false;
       try {
           isAuto = sqlSession.getConnection().getAutoCommit();
           sqlSession.getConnection().setAutoCommit(false);
       } catch (SQLException e) {
           e.printStackTrace();
       }
 
       try {
           User user = new User();
           user.setUsername("李四");
           userMapper.deleteUserByName(user);
           sqlSession.commit();
       } catch (Exception e) {
           sqlSession.rollback();
           e.printStackTrace();
       }
 
       try {
           sqlSession.getConnection().setAutoCommit(isAuto);
       } catch (SQLException e) {
           e.printStackTrace();
       }
 
   }
 
@Test
   public void testUpdateUserNameById(){
       boolean isAuto = false;
       try {
           isAuto = sqlSession.getConnection().getAutoCommit();
           sqlSession.getConnection().setAutoCommit(false);
       } catch (SQLException e) {
           e.printStackTrace();
       }
 
       try {
           User user = new User();
           user.setId(1);
           user.setUsername("黄柯");
           userMapper.updateUserNameById(user);
           sqlSession.commit();
       } catch (Exception e) {
           sqlSession.rollback();
           e.printStackTrace();
       }
 
       try {
           sqlSession.getConnection().setAutoCommit(isAuto);
       } catch (SQLException e) {
           e.printStackTrace();
       }
 
   }

3.2 Mybatis案例(返回结果)

数据库:

drop database if exists mybatisdemo;
create  database mybatisdemo;
use mybatisdemo;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id`         int(11) NOT NULL AUTO_INCREMENT,
  `username`  varchar(32) NOT NULL COMMENT '用户名称',
  `birthday`  date DEFAULT NULL COMMENT '生日',
  `sex`       char(1) DEFAULT NULL COMMENT '性别',
  `address`   varchar(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
INSERT INTO `user` VALUES ('1', '张三', '2018-07-10', '1', '北京');
INSERT INTO `user` VALUES ('2', '李四', '2018-07-10', '0', '上海');
INSERT INTO `user` VALUES ('3', '王五', '2018-07-10', '1', '广州');
INSERT INTO `user` VALUES ('4', '王六', '2018-07-10', '1', '深圳');
INSERT INTO `user` VALUES ('5', '王八', '2018-07-10', '1', '上海');

案例1:返回单一实体对象

  • Mapper 接口方法
User selectUserById(int id);
  • Mapper 映射文件
<select id="selectUserById" parameterType="_int" resultMap="userMap">
        select * from `user` where id = #{id}
    </select>
  • 测试代码
User user = userMapper.selectUserById(2);
System.out.println(user);

案例2:返回集合实体对象

  • Mapper 接口方法
List<User> selectAllUser();
  • Mapper 映射文件
<select id="selectAllUser" resultMap="userMap">
    select * from `user`
</select>
  • 测试代码
List<User> users = userMapper.selectAllUser();
for (User user : users) {
   System.out.println(user);
}

案例3:返回Map的实体对象集合

  • Mapper 接口方法
@MapKey("id")
Map<Integer,User> selectAllMapUser();
  • Mapper 映射文件
<select id="selectAllMapUser"  resultMap="userMap">
    select * from `user`
</select>
  • 测试代码

Map<Integer, User> userMap = userMapper.selectAllMapUser();
 //遍历Map
Set<Integer> keys = userMap.keySet();
for (Integer key : keys) {
     System.out.println(key + " --> " +userMap.get(key));
}

案例4:返回单个对象的Map形式

  • Mapper 接口方法
Map<String,Object> selectOneMapUser(int id);
  • Mapper 映射文件
<select id="selectOneMapUser"  resultType="HashMap">
    select * from `user` where id= #{id}
</select>
  • 测试代码
Map<String, Object> integerUserMap = userMapper.selectOneMapUser(1);
System.out.println(integerUserMap);

案例5:返回Map形式的数据行

  • Mapper 接口方法
List<  Map<String,Object>  > selectAllMapCol();
  • Mapper 映射文件
<select id="selectAllMapCol"  resultType="map">
        select * from `user`
  </select>
  • 测试代码

List<Map<String, Object>> maps = userMapper.selectAllMapCol();
for (Map<String, Object> map : maps) {
    Set<String> cols = map.keySet();
    for (String col:cols) {
        System.out.print(col+ ":"+map.get(col) +"\t");
    }
    System.out.println();
}

4. 动态SQL

  • 动态 SQL是MyBatis强大特性之一。极大的简化我们拼装SQL的操作
  • 动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似
  • MyBatis 采用功能强大的基于 OGNL 的表达式来简化操作
  • OGNL( Object Graph Navigation Language )对象图导航语言,这是一种强大的表达式语言,通过它可以非常方便的来操作对象属性。 类似于我们的EL,SpEL等

访问对象属性: person.name

调用方法: person.getName()

调用静态属性/方法: @java.lang.Math@PI @java.util.UUID@randomUUID()

调用构造方法: new com.atguigu.bean.Person(‘admin’).name

运算符: +,-*,/,%

逻辑运算符: in,not in,>,>=,<,<=,==,!=

注意:xml中特殊符号如”,>,<等这些都需要使用转义字符

4.1 if where

  • If用于完成简单的判断.
  • Where用于解决SQL语句中where关键字以及条件中第一个and或者or的问题
<select id="getEmpsByConditionIf" resultType="net.suncaper.mybatis.domain.Employee">
        select id , last_name ,email  , gender 
        from tbl_employee
        <where>
            <if test="id!=null">
                and id = #{id}
            </if>
            <if test="lastName!=null && lastName!=""">
                and last_name = #{lastName}
            </if>
            <if test="email!=null and email.trim()!=''">
                and email = #{email}
            </if>
            <if test=""m".equals(gender) or "f".equals(gender)">
                and gender = #{gender}
            </if>
        </where>
</select>

4.2 trim

  • Trim 可以在条件判断完的SQL语句前后 添加或者去掉指定的字符
  • prefix: 添加前缀
  • prefixOverrides: 去掉前缀
  • suffix: 添加后缀
  • suffixOverrides: 去掉后缀
<select id="getEmpsByConditionTrim" resultType="net.suncaper.mybatis.domain.Employee">
        select id , last_name ,email  , gender 
        from tbl_employee
        <trim prefix="where"  suffixOverrides="and">
            <if test="id!=null">
                 id = #{id} and
            </if>
            <if test="lastName!=null && lastName!=""">
                 last_name = #{lastName} and
            </if>
            <if test="email!=null and email.trim()!=''">
                 email = #{email} and
            </if>
            <if test=""m".equals(gender) or "f".equals(gender)">
                gender = #{gender}
            </if>
        </trim>
</select>

4.3 set

  • set 主要是用于解决修改操作中SQL语句中可能多出逗号的问题
<update id="updateEmpByConditionSet">
        update  tbl_employee 
        <set>
            <if test="lastName!=null && lastName!=""">
                 last_name = #{lastName},
            </if>
            <if test="email!=null and email.trim()!=''">
                 email = #{email} ,
            </if>
            <if test=""m".equals(gender) or "f".equals(gender)">
                gender = #{gender}
            </if>
        </set>
         where id =#{id}
    </update>

4.4 choose(when、otherwise)

  • choose 主要是用于分支判断,类似于java中的switch case,只会满足所有分支中的一个
<select id="getEmpsByConditionChoose" resultType="net.suncaper.mybatis.domain.Employee">
        select id ,last_name, email,gender from tbl_employee
        <where>
            <choose>
                <when test="id!=null">
                    id = #{id}
                </when>
                <when test="lastName!=null">
                    last_name = #{lastName}
                </when>
                <when test="email!=null">
                    email = #{email}
                </when>
                <otherwise>
                     gender = 'm'
                </otherwise>
            </choose>
        </where>
</select>

4.5 for each

  • for each 主要用于循环迭代
    • collection: 要迭代的集合

    • item: 当前从集合中迭代出的元素

    • open: 开始字符

    • close:结束字符

    • separator: 元素与元素之间的分隔符

    • index:

      • 迭代的是List集合: index表示的当前元素的下标

      • 迭代的Map集合: index表示的当前元素的key

<select id="getEmpsByConditionForeach" resultType="net.suncaper.mybatis.domain.Employee">
         select id , last_name, email ,gender from tbl_employee where  id in
         <foreach collection="ids" item="curr_id" open="(" close=")" separator="," >
                #{curr_id}
         </foreach>
</select>

4.6 sql

  • sql 标签是用于抽取可重用的sql片段,将相同的,使用频繁的SQL片段抽取出来,单独定义,方便多次引用.
  • 抽取SQL:
<sql id="selectSQL">
        select id , last_name, email ,gender from tbl_employee
</sql>
  • 引用SQL:
<include refid="selectSQL"></include>

5. 关联查询

5.1一对一关联

需要
根据班级id查询班级信息(带老师的信息)
创建表和数据

CREATE TABLE teacher(
    t_id INT PRIMARY KEY AUTO_INCREMENT,
    t_name VARCHAR(20)
);
CREATE TABLE class(
    c_id INT PRIMARY KEY AUTO_INCREMENT,
    c_name VARCHAR(20),
    teacher_id INT
);
ALTER TABLE class ADD CONSTRAINT fk_teacher_id FOREIGN KEY (teacher_id) REFERENCES teacher(t_id);  
 
INSERT INTO teacher(t_name) VALUES('LS1');
INSERT INTO teacher(t_name) VALUES('LS2');
 
INSERT INTO class(c_name, teacher_id) VALUES('bj_a', 1);
INSERT INTO class(c_name, teacher_id) VALUES('bj_b', 2);

定义实体类

public class Teacher {
    private int id;
    private String name;
}
public class Classes {
    private int id;
    private String name;
    private Teacher teacher;
}

定义sql映射文件ClassMapper.xml

<select id="getClass" parameterType="int" resultMap="ClassResultMap">
    select * from class c, teacher t where c.teacher_id=t.t_id and  c.c_id=#{id}
</select>
<resultMap type="_Classes" id="ClassResultMap">
    <id property="id" column="c_id"/>
    <result property="name" column="c_name"/>
    <association property="teacher" javaType="_Teacher">
        <id property="id" column="t_id"/>
        <result property="name" column="t_name"/>
    </association>
</resultMap>

5.2一对多关联

需求
根据classId查询对应的班级信息,包括学生,老师

创建表和数据

CREATE TABLE student(
    s_id INT PRIMARY KEY AUTO_INCREMENT,
    s_name VARCHAR(20),
    class_id INT
);
INSERT INTO student(s_name, class_id) VALUES('xs_A', 1);
INSERT INTO student(s_name, class_id) VALUES('xs_B', 1);
INSERT INTO student(s_name, class_id) VALUES('xs_C', 1);
INSERT INTO student(s_name, class_id) VALUES('xs_D', 2);
INSERT INTO student(s_name, class_id) VALUES('xs_E', 2);
INSERT INTO student(s_name, class_id) VALUES('xs_F', 2);

定义实体类

public class Student {
    private int id;
    private String name;
}
 
public class Classes {
    private int id;
    private String name;
    private Teacher teacher;
    private List<Student> students;
}

定义sql映射文件ClassMapper.xml

<select id="getClass3" parameterType="int" resultMap="ClassResultMap3">
    select * from class c, teacher t,student s where c.teacher_id=t.t_id and c.C_id=s.class_id and  c.c_id=#{id}
</select>
<resultMap type="_Classes" id="ClassResultMap3">
    <id property="id" column="c_id"/>
    <result property="name" column="c_name"/>
    <association property="teacher" column="teacher_id" javaType="Teacher">
        <id property="id" column="t_id"/>
        <result property="name" column="t_name"/>
    </association>
    <!-- ofType指定students集合中的对象类型 -->
    <collection property="students" ofType="Student">
        <id property="id" column="s_id"/>
        <result property="name" column="s_name"/>
    </collection>
</resultMap>

6. Mybatis 缓存

  • MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率

  • MyBatis系统中默认定义了两级缓存
    一级缓存

    二级缓存

  • 默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。

  • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。

  • 为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

6.1 一级缓存的使用

  • 一级缓存(local cache), 即本地缓存, 作用域默认为sqlSession。当 Session flush 或 close 后, 该 Session 中的所有 Cache 将被清空。
  • 本地缓存不能被关闭, 但可以调用 clearCache() 来清空本地缓存, 或者改变缓存的作用域.
  • 在1之后, 可以配置本地缓存的作用域. 在 mybatis.xml 中配置
    在这里插入图片描述
  • 一级缓存的工作机制
    • 同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中
    • key: hashCode+查询的SqlId+编写的sql查询语句+参数

6.2一级缓存失效的几种情况

  • 不同的SqlSession对应不同的一级缓存
  • 同一个SqlSession但是查询条件不同
  • 同一个SqlSession两次查询期间执行了任何一次增删改操作
  • 同一个SqlSession两次查询期间手动清空了缓存

6.3二级缓存的使用

  • 二级缓存(second level cache),全局作用域缓存
  • 二级缓存默认不开启,需要手动配置
  • MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口
  • 二级缓存在 SqlSession 关闭或提交之后才会生效
  • 二级缓存使用的步骤:
    • 全局配置文件中开启二级缓存
    • 需要使用二级缓存的映射文件处使用cache配置缓存
    • 注意:POJO需要实现Serializable接口
  • 二级缓存相关的属性
    • eviction=“FIFO”:缓存回收策略:

      • LRU – 最近最少使用的:移除最长时间不被使用的对象。

      • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。

      • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。

      • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。默认的是 LRU。

    • flushInterval:刷新间隔,单位毫秒。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新

    • size:引用数目,正整数。代表缓存最多可以存储多少个对象,太大容易导致内存溢出

    • readOnly:只读,true/false

      • true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
      • false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。

6.4 缓存的相关属性设置

  • 全局setting的cacheEnable:
    配置二级缓存的开关,一级缓存一直是打开的。

  • select标签的useCache属性:
    配置这个select是否使用二级缓存。一级缓存一直是使用的

  • sql标签的flushCache属性:
    增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。

    查询默认 flushCache=false。

  • clearCache():只是用来清除一级缓存。

6.5 整合第三方缓存

  • 为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
  • EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider
  • 整合EhCache缓存的步骤:
    • 导入ehcache包,以及整合包,日志包
      ehcache-core-2.6.8.jar、mybatis-ehcache-1.0.3.jar

    • slf4j-api-1.6.1.jar、slf4j-log4j12-1.6.2.jar

    • 编写xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
 <!-- 磁盘保存路径 -->
 <diskStore path="D:\atguigu\ehcache" />
  
 <defaultCache
   maxElementsInMemory="1000"
   maxElementsOnDisk="10000000"
   eternal="false"
   overflowToDisk="true"
   timeToIdleSeconds="120"
   timeToLiveSeconds="120"
   diskExpiryThreadIntervalSeconds="120"
   memoryStoreEvictionPolicy="LRU">
 </defaultCache>
</ehcache>
  
<!--
属性说明:
l diskStore:指定数据在磁盘中的存储位置。
l defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
  
以下属性是必须的:
l maxElementsInMemory - 在内存中缓存的element的最大数目
l maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
l eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
l overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
  
以下属性是可选的:
l timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
l timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
 diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
l diskPersistent -VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
l diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
l memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
 -->
  • 配置cache标签
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>

7. Mybatis 代码反向生成器(MBG)

7.1、插件的使用

描述: mybatis generator 的作用是根据数据库自动生成 实体类、Dao接口、Mapper 映射文件。
数据库:

create table product(
id int primary key AUTO_INCREMENT,
pname varchar(20),
price double,
category_id varchar(32)
);
INSERT INTO product(pname,price,category_id) VALUES('联想',5000,'电脑');
INSERT INTO product(pname,price,category_id) VALUES('海尔',3000,'洗衣机');
INSERT INTO product(pname,price,category_id) VALUES('雷神',5000,'笔记本');
INSERT INTO product(pname,price,category_id) VALUES('JACK JONES',800,'服饰');
INSERT INTO product(pname,price,category_id) VALUES('真维斯',200,'服饰');
INSERT INTO product(pname,price,category_id) VALUES('花花公子',440,'服饰');
INSERT INTO product(pname,price,category_id) VALUES('劲霸',2000,'服饰');
INSERT INTO product(pname,price,category_id) VALUES('香奈儿',800,'奢侈品');
INSERT INTO product(pname,price,category_id) VALUES('相宜本草',200,'亲民品');
INSERT INTO product(pname,price,category_id) VALUES('面霸',5,'垃圾');
INSERT INTO product(pname,price,category_id) VALUES('好想你枣',56,'零食');
INSERT INTO product(pname,price,category_id) VALUES('香飘飘奶茶',1,'毒药');
INSERT INTO product(pname,price,category_id) VALUES('果9',1,NULL);

步骤1:在pom.xml 中添加MBG插件

  • pom.xml 中添加以下代码
<plugin>
       <groupId>org.mybatis.generator</groupId>
       <artifactId>mybatis-generator-maven-plugin</artifactId>
       <version>1.4.0</version>
       <configuration>
             <configurationFile>${basedir}/src/main/resources/mybatis/generator/generatorConfig.xml</configurationFile>
            <!-- 设置覆盖原有的文件:第二次执行的时候 -->
            <overwrite>true</overwrite>
            <!-- 输出一些日志信息-->
            <verbose>true</verbose>
       </configuration>
 
        <dependencies>
                    <!-- maven 插件使用依赖包要独立设置 -->
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>5.1.48</version>
                    </dependency>
       </dependencies>
</plugin>

步骤2:编写 generatorConfig.xml 文件

  • 在 src/main/resources/mybatis/generator目录下新建一个 generatorConfig.xml
    • 设置实体类、Mapper 映射文件、Dao 接口对应位置
<?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>
    <context id="MySQLTables" targetRuntime="MyBatis3">
        <!--  虽然插件中配置了<overwrite>true</overwrite>,但是只对java 文件有效,对mapper文件无效.特定使用 UnmergeableXmlMappersPlugin 才可以覆盖xml文件 -->
        <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />
        <!--关闭注释的生成-->
        <commentGenerator>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <!--覆盖生成XML文件-->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3306/mybatisDemo"
                        userId="root"
                        password="root">
        </jdbcConnection>
 
        <javaTypeResolver >
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>
        <!-- 实体类-->
        <javaModelGenerator targetPackage="net.suncaper.demo1.domain" targetProject="src/main/java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!--Mapper 映射文件-->
        <sqlMapGenerator targetPackage="mybatis.mapper"  targetProject="src/main/resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
         <!--Mapper 到接口 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="net.suncaper.demo1.mapper"  targetProject="src/main/java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>
 
        <!--  对应的表生成对应的 实体类 -->
        <table tableName="product" domainObjectName="Product"/>
 
      <!--  <table schema="DB2ADMIN" tableName="ALLTYPES" domainObjectName="Customer" >
            <property name="useActualColumnNames" value="true"/>
            <generatedKey column="ID" sqlStatement="DB2" identity="true" />
            <columnOverride column="DATE_FIELD" property="startDate" />
            <ignoreColumn column="FRED" />
            <columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" />
        </table>-->
 
    </context>
</generatorConfiguration>
  • Mapper 映射文件
<select id="selectAllUser" resultMap="userMap">
    select * from `user`
</select>
  • 测试代码
List<User> users = userMapper.selectAllUser();
for (User user : users) {
   System.out.println(user);
}

步骤3:通过运行maven 插件对应的方法

  • 打开Idea 中maven 的视图 , Plugins → mybatis-generator → mybatis-generator:generator
  • 截图
    在这里插入图片描述

8. PageHelper分页插件

链接: 官方文档.

8.1. 引入分页插件

在 pom.xml 中添加下面的依赖:

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

在MyBatis全局配置文件中配置分页插件

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

8.2.分页插件参数介绍

  • helperDialect:分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置helperDialect属性来指定分页插件使用哪种方言。配置时,可以使用下面的缩写值:
    oracle,mysql,mariadb,sqlite,hsqldb,postgresql,db2,sqlserver,informix,h2,sqlserver2012,derby
  • offsetAsPageNum:默认值为 false,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为 true 时,会将 RowBounds 中的 offset 参数当成 pageNum 使用,可以用页码和页面大小两个参数进行分页。
  • rowBoundsWithCount:默认值为false,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为true时,使用 RowBounds 分页会进行 count 查询。
  • pageSizeZero:默认值为 false,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。
  • reasonable:分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。
  • params:为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero。
  • supportMethodsArguments:支持通过 Mapper 接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。 使用方法可以参考测试代码中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和 ArgumentsObjTest。
  • autoRuntimeDialect:默认值为 false。设置为 true 时,允许在运行时根据多数据源自动识别对应方言的分页 (不支持自动选择sqlserver2012,只能使用sqlserver)。
  • closeConn:默认值为 true。当使用运行时动态数据源或没有设置 helperDialect 属性自动获取数据库类型时,会自动获取一个数据库连接, 通过该属性来设置是否关闭获取的这个连接,默认true关闭,设置为 false 后,不会关闭获取的连接,这个参数的设置要根据自己选择的数据源来决定。

8.3.Page对象的使用

在查询之前通过PageHelper.startPage(页码,条数)设置分页信息,紧跟在这条信息后面的查询语句即为分页查询。

Page<Object> objects = PageHelper.startPage(2, 5);
List<Product> products = mapper1.selectByExample(productExample);

4.PageInfo对象的使用

在查询完数据后,使用PageInfo对象封装查询结果,可以获取更详细的分页信息以及可以完成分页逻辑。

Page<Object> objects = PageHelper.startPage(2, 5);
List<Product> products = mapper1.selectByExample(productExample);
PageInfo pageInfo=new PageInfo(products);

5.PageInfo对象参数介绍

//当前页
private int pageNum;
//每页的数量
private int pageSize;
//当前页的数量
private int size;
 
//由于startRow和endRow不常用,这里说个具体的用法
//可以在页面中"显示startRow到endRow 共size条数据"
 
//当前页面第一个元素在数据库中的行号
private int startRow;
//当前页面最后一个元素在数据库中的行号
private int endRow;
//总记录数
private long total;
//总页数
private int pages;
//结果集
private List<T> list;
 
//前一页
private int prePage;
//下一页
private int nextPage;
 
//是否为第一页
private boolean isFirstPage = false;
//是否为最后一页
private boolean isLastPage = false;
//是否有前一页
private boolean hasPreviousPage = false;
//是否有下一页
private boolean hasNextPage = false;
//导航页码数
private int navigatePages;
//所有导航页号
private int[] navigatepageNums;
//导航条上的第一页
private int navigateFirstPage;
//导航条上的最后一页
private int navigateLastPage;

9 .Spring集成Mybatis

9.1.创建数据库

CREATE TABLE s_user(
       user_id INT AUTO_INCREMENT PRIMARY KEY,
       user_name VARCHAR(30),
       user_birthday DATE,
       user_salary DOUBLE
)
 
INSERT INTO s_user VALUE(1,"张三","2019-02-13",800);
INSERT INTO s_user VALUE(2,"李四","2019-02-13",123);
INSERT INTO s_user VALUE(3,"王五","2019-02-13",998);
INSERT INTO s_user VALUE(4,"赵六","2019-02-13",2000);

9.2.mybatis逆向工程

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>
    <context id="MySQLTables" targetRuntime="MyBatis3">
        <!--  虽然插件中配置了<overwrite>true</overwrite>,但是只对java 文件有效,对mapper文件无效.特定使用 UnmergeableXmlMappersPlugin 才可以覆盖xml文件 -->
        <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/>
        <!--关闭注释的生成-->
        <commentGenerator>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <!--覆盖生成XML文件-->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3306/mybatisDemo"
                        userId="root"
                        password="root">
        </jdbcConnection>
 
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
        <!-- 实体类-->
        <javaModelGenerator targetPackage="net.suncaper.demo.domain" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <!--Mapper 映射文件-->
        <sqlMapGenerator targetPackage="mybatis.mapper" targetProject="src/main/resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>
        <!--Mapper 到接口 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="net.suncaper.demo.mapper" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>
 
        <!--  对应的表生成对应的 实体类 -->
        <table tableName="s_user" domainObjectName="User"/>
 
 
    </context>
</generatorConfiguration>

9.3.sping-mybatis配置文件

spring-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
    <!-- 1. 数据源 : DriverManagerDataSource -->
    <bean id="dataSource"
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatisDemo"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
 
    <!--
        2. 指定数据源和配置文件路径
     -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:conf/mybatis-config.xml"/>
        <property name="mapperLocations">
            <list>
                <value>classpath:mybatis/mapper/*.xml</value>
            </list>
        </property>
    </bean>
    <!--
        3. 扫描DAO接口所在包名,Spring会自动查找其下的类,并将其定义为一个Spring Bean
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="net.suncaper.demo.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>
 
 
</beans>

9.4.mybatis配置文件

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>
 
    <!-- Spring整合myBatis后,这个配置文件基本可以不要了-->
 
    <!-- 设置外部配置文件 -->
 
    <!-- 设置类别名 -->
    <typeAliases>
        <package name="net.suncaper.demo.domain"/>
    </typeAliases>
    <!-- 设置数据库连接环境 -->
 
    <!-- 映射文件 -->
</configuration>

9.5.编写测试类

Test.java

import net.suncaper.demo.domain.User;
import net.suncaper.demo.mapper.UserMapper;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
@RunWith(SpringJUnit4ClassRunner.class) //使用Springtest测试框架
@ContextConfiguration("/conf/spring-config.xml")
public class Test {
    @Autowired  //自动注入
    private  UserMapper userMapper;
 
    @org.junit.Test
    public void hh(){
        User user = userMapper.selectByPrimaryKey(1);
        System.out.println(user);
    }

9.6.说明

Mybatis中的重要对象
SqlSession
SqlSession是用于执行持久化操作的对象,类似于JDBC中的Connection。它提供了面向数据库执行SQL命令所需的所有方法,可以通过SqlSession实例直接运行已映射的SQL语句。SqlSession对应着一次数据库回话。由于数据库会话不是永久的,因此SqlSession的生命周期也不应该是永久的。相反,在每次访问数据库时都需要创建它。需要注意的是,每个线程都有自己的SqlSession实例,SqlSession实例不能被共享,也不是线程安全的。因此最佳的作用域范围是request作用域或者方法体作用域内。

SqlSessionFactory
每个数据库对应一个 SqlSessionFactory。所以,如果你想连接两个数据库,你需要创建两个 SqlSessionFactory 实例。以此类推。

关于SqlSession,SqlSessionFactory,SqlSessionFactoryBean的关系。
Mybatis核心是获取 SqlSession 实例。要获得 SqlSession 实例,则需要依赖 SqlSessionFactory 实例,通过openSession()方法获得。而 SqlSessionFactory 是 SqlSessionFactoryBuilder 依据 MyBatis 配置文件中的数据源、Sql映射文件等信息来构建的。在 MyBatis 中,SqlSessionFactory 的实例需要使用 SqlSessionFactoryBuilder 创建;而在集成环境中,则可以使用 MyBatis-Spring 整合包中的 SqlSessionFactoryBean 来代替。SqlSessionFactoryBean 封装了使用 SqlSessionFactoryBuilder 创建 SqlSessionFactory 的过程,我们可以在 Spring 中以配置文件的形式,通过配置 SqlSessionFactoryBean 获得 SqlSessionFactory 实例。

@Autowired报错
解决办法是:降低Autowired检测的级别,将Severity的级别由之前的error改成warning或其它可以忽略的级别。
在这里插入图片描述
@RunWith(SpringJUnit4ClassRunner.class)
让测试运行于spring测试环境

@ContextConfiguration
用来指定加载的Spring配置文件的位置,会加载默认配置文件。

@ContextConfiguration 注解有以下两个常用的属性:locations:可以通过该属性手工指定 Spring 配置文件所在的位置,可以指定一个或多个 Spring 配置文件用,分开。如下所示: @ContextConfiguration(locations={“aa/aa.xml”,” aa/bb.xml”})

inheritLocations:是否要继承父测试用例类中的 Spring 配置文件,默认为 true。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值