Mybatis基础

1. Mybatis简介

为什么要使用Mybatis

MyBatis是一个【半自动化】的持久化层框架。

JDBC

–SQL夹在Java代码块里,耦合度高导致硬编码内伤

–维护不易且实际开发需求中sql是有变化,频繁修改的情况多见

Hibernate和JPA

–长难复杂SQL,对于Hibernate而言处理也不容易

–内部自动生产的SQL,不容易做特殊优化。

–基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难。导致数据库性能下降。

对开发人员而言,核心sql还是需要自己优化

sql和java编码分开,功能边界清晰,一个专注业务、一个专注数。

2. Mybaits-HelloWorld

–1创建一张测试表

–2创建对应的javaBean

–3创建mybatis配置文件,

-4创建sql映射文件

-5在全局配置文件注册

–6测试

1、导包,创建一张测试表

mybatis-3.4.1
mysql-connector-java-5.1.37-bin
log4j-1.2.17

2、创建对应的javaBean与dao接口
Employee:

public class Employee {

    private Integer id;
    private String empName;
    private Integer gender;
    private String email;
	//省略有参无参、get/set()、toString()
}

EmployeeDao接口:

public interface EmployeeDao {

    //按照id查询员工
    public Employee getEmpById(Integer id);
}

3、创建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>
    <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/mybatis_achang"/>
                <property name="username" value="root"/>
                <property name="password" value="00000"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="org/mybatis/example/BlogMapper.xml"/>
    </mappers>
</configuration>

4、创建sql映射文件

EmployeeDao.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.achang.dao.EmployeeDao">
    <select id="getEmpById" resultType="com.achang.bean.Employee">
        select * from t_employee where id = #{id}
    </select>
    <!--增删改不用写返回值类型-->
    <update id="updateEmp" >
        update t_employee set empname = #{empName},gender = #{gender},email = #{email} where id = #{id}
    </update>
    <delete id="deleteEmpById">
        delete from t_employee where id = #{id}
    </delete>
    <insert id="insertEmp">
        insert into t_employee(empname,gender,email)
                values(#{empName},#{gender},#{email})
    </insert>
</mapper>

5、在全局配置文件注册
在mybatis_config.xml中配置:

<!--mappers标签:引入自己编写的每一个接口的xml实现文件
		resource属性:引入xml实现文件的位置
-->
<mappers>
	<mapper resource="EmployeeDao.xml"/>
</mappers>

6、测试

查询

@Test
public void test1() throws IOException {
    //1、根据全局配置文件创建出一个sqlSessionFactory
    //SqlSessionFactory:是SqlSession工厂,负责创建SqlSession对象
    //SqlSession对象:sql会话---(代表和数据库的一次会话)
    String resource = "mybatis_config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

    Employee employee = null;
    SqlSession openSession = null;
    try {
        //2、获取和数据库的一次会话;与getConnection();拿到一条连接
        sqlSessionFactory.openSession();
        //3、使用SqlSession操作数据库,获取到dao接口的实现
        EmployeeDao employeeDaoImpl = openSession.getMapper(EmployeeDao.class);
        //4、拿到dao接口impl实现类后,调用相对于的方法即可
        employee = employeeDaoImpl.getEmpById(1);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        openSession.close();
    }
    System.out.println(employee);//Employee{id=1, empName='admin', gender=0, email='admin@qq.com'}
}

增删改

增删改需要设置事务

方式一、—要在方法中最后try-catch-finally关闭资源,增删改需要手动提交事务

方式二、—openSession(true);设置是否自动提交事务,true为自动提交,false为不自动提交(例子:修改方法)

public class EmployeeTest {
	//工厂在类中创建一次即可
    SqlSessionFactory sqlSessionFactory;
    //每次调用自动的执行,一次会话一次连接
    @Before
    public void initSqlSessionFactory() throws IOException {
        String resource = "mybatis_config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    //查询
    @Test
    public void test1() throws IOException {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        EmployeeDao mapper = sqlSession.getMapper(EmployeeDao.class);
        Employee empById = mapper.getEmpById(3);
        System.out.println(empById);
    }

    //修改
    @Test
    public void test2() throws IOException {
        //openSession(true)设置自动提交事务
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        int updateEmp = 0;
        try {
            EmployeeDao mapper = sqlSession.getMapper(EmployeeDao.class);
            updateEmp = mapper.updateEmp(new Employee(3, "欧尼", 1, "achang@qq.com"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.close();
        }
        System.out.println(updateEmp);
    }

    //新增
    @Test
    public void test3() throws IOException {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        int insertEmp = 0;
        try {
            EmployeeDao mapper = sqlSession.getMapper(EmployeeDao.class);
            insertEmp = mapper.insertEmp(new Employee(null, "我我", 0, "sisi@qq.com"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.commit();
            sqlSession.close();
        }
        System.out.println(insertEmp);

    }

    //删除
    @Test
    public void test4() throws IOException {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        boolean empById = false;
        try {
            EmployeeDao mapper = sqlSession.getMapper(EmployeeDao.class);
            empById = mapper.deleteEmpById(8);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlSession.commit();
            sqlSession.close();
        }
        System.out.println(empById);
    }
}

3. 全局配置文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8FzmsQT1-1608107011637)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201215205836022.png)]

3.1 properties(属性)

作用:通过properties标签引入外部内容

properties标签:和Spring的context:property-placeholder;引用外部配置文件
resource属性:从类路径下引入
url属性:引用磁盘路径或网络路径

dbconfig.properties:

username=root
password=00000
url=jdbc:mysql://localhost:3306/mybatis_achang
driver=com.mysql.jdbc.Driver

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>
    <properties resource="dbconfig.properties"/>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--${}取出配置文件中的值-->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="EmployeeDao.xml"/>
    </mappers>

</configuration>

3.2 settings(设置)

settings是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

img

img

**举例:**mapUnderscoreToCamelCase

数据库字段名与bean对象属性对应驼峰原则

数据库:login_account

javabean:loginAccount

<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
</configuration>

3.3 typeAliases(类型别名)

推荐还是使用全类名!!!

类型别名:为常用的类型起别名
        typeAlias属性:就是为一个javaBean起别名;别名默认就是类名(不区分大小写),配置文件中就可以用别名了
               alias属性:指定一个别名
        package属性:批量起别名
                name属性:指定一个包名,默认别名就是类名
                @alias()注解:起别名
<typeAliases>
    <typeAlias type="com.achang.bean.Employee" alias="emp"/>//起别名
    <package name="com.achang.bean"/>//批量起别名
</typeAliases>

3.4 typeHandlers(类型处理器)

  • 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
<typeHandlers>
    <!--自定义好的类型处理器就这么配置上就好-->
    <typeHandler handler="自定义类型处理器全类名"
</typeHandlers>

img

3.5 plugins(插件)

  • 插件是MyBatis提供的一个非常强大的机制,我们可以通过插件来修改MyBatis的一些核心行为。插件通过动态代理机制,可以介入四大对象的任何一个方法的执行。后面会有专门的章节我们来介绍mybatis运行原理以及插件

3.6 environments(环境)

default属性:默认使用哪个环境;填写某个environment标签的id
environment标签:配置一个具体的环境;每一个环境都需要一个事务管理器和数据源
id属性:当前环境的唯一标识
transactionManager标签:事务管理器

后来数据源、事务控制管理都Spring来做;

default属性:默认使用哪个环境;填写某个environment标签的id
environment标签:配置一个具体的环境;每一个环境都需要一个事务管理器和数据源
	id属性:当前环境的唯一标识
	transactionManager标签:事务管理器

后来数据源、事务控制管理都Spring来做;
<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <!--${}取出配置文件中的值-->
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
</environments>

3.7 mappers(映射器)

EmployeeAnnotationDao:在dao接口头注解上写对应的sql语句

public interface EmployeeAnnotationDao {

    @Select("select * from t_employee where id = #{id}")
    public Employee getEmpById(Integer id);

    @Update("update t_employee set empname = #{empName},gender = #{gender},email = #{email} where id = #{id}\n")
    public int updateEmp(Employee employee);

    @Delete("delete from t_employee where id = #{id}")
    public boolean deleteEmpById(Integer id);

    @Insert("insert into t_employee(empname,gender,email)" +
            "values(#{empName},#{gender},#{email}))")
    public int insertEmp(Employee employee);

}

使用class导入接口注解头sql语句类

<!--写好的sql映射文件,需要使用mappers注册-->
<mappers>
    <mapper class="com.achang.dao.EmployeeAnnotationDao"/>
    <package name=""/>
</mappers>

4. Sql映射文件

—cache –命名空间的二级缓存配置
—cache-ref – 其他命名空间缓存配置的引用。
—resultMap – 自定义结果集映射
—parameterMap – 已废弃!老式风格的参数映射,原本是做复杂参数映射的
—sql –抽取可重用语句块。
—insert – 映射插入语句
—update – 映射更新语句
—delete – 映射删除语句
—select – 映射查询语句

4.1 增删改标签

—insert – 映射插入语句
—update – 映射更新语句
—delete – 映射删除语句

id要对应实现的方法名

<select id="getEmpById" resultType="com.achang.bean.Employee" >
    select * from t_employee where id = #{id}
</select>

img

数据库支持获取主键

<!--让MyBatis自动的将自增的id赋值给传入的employee对象的id属性
        useGeneratedKeys属性:开启自动赋值id功能
        keyProperty属性:将刚才自增的id封装给那个属性
-->
<insert id="insertEmp" useGeneratedKeys="true" keyProperty="id">
    insert into t_employee(empname,gender,email)values(#{empName},#{gender},#{email})
</insert>

4.2 参数传递

1、单个参数
基本类型:
取值:#{随便写}
2、多个参数:
取值:#{参数名}是无效的的
0,1(参数索引)或者param1,param2(第几个参数paramN)
原因:
只要传入多个参数,mybatis会自动的将这些参数封装到一个map中;封装时使用的key就是参数的索引和参数的第几个标识
@param:为参数指定封装map时的key;命名参数
我们可以告诉mybatis,封装参数map的时候别乱来
3、传入map
封装多个参数为map,直接传递
4、传入bean
取值:#{bean属性名}

4.3 #{}和¥{}

•实际上通常被设置的是:

可能为空的列名指定 jdbcType,如#{key, jdbcType=int} ,因为oracle不知道如何处理空值

#{key}:获取参数的值,预编译到SQL中。安全。

${key}:获取参数的值,拼接到SQL中。有SQL注入问题。ORDER BY ${name}

4.4 查询返回List

EmployeeDao:

public interface EmployeeDao {
	public List<Employee> getAllEmps();
}

dao.xml

<!--resultType:如果返回的是集合,写的是集合里面元素的类型-->
<select id="getAllEmps" resultType="com.achang.bean.Employee">
    select * from t_employee
</select>

4.5 查询返回map

1)单条记录返回Map

dao

public interface EmployeeDao {
    /****
     *  列名为Key,值为value
     */
    public Map<String,Object> getEmpByIdReturnMap(Integer id);
}

dao.xml

resultType属性中的map已经被mybatis自动写入别名为map了

<select id="getEmpByIdReturnMap" resultType="map">
    select * from t_employee where id = #{id}
</select>

2)多条记录返回Map

通过@MapKey()注解来告诉mybatis数据库中哪个字段作为key主键来,封装value

dao

public interface EmployeeDao {
    //key是记录的主键,value就是记录封装好的对象
    //@MapKey根据数据库里面的哪个字段作为key来查询封装value
    @MapKey("id")
    public Map<Integer,Employee> getEmpsByIdReturnMap();
}

dao.xml

<!--查询多个的情况下,resultType属性写value封装的元素类型-->
<select id="getEmpsByIdReturnMap" resultType="com.achang.bean.Employee">
    select * from t_employee
</select>

4.6 自定义封装规则resultMap

reesultMap标签自定义封装
type属性:指定引入哪个javabaen与数据库封装对应
id属性:指定这个自定义封装的id,便于其他引用
id标签:指定主键
result标签:指定其他封装对象
property属性:指定javabean属性名
column属性:指定数据库字段名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YSc9W2hU-1608107011660)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201216161912606.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1tXCcyvk-1608107011661)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201216162225702.png)]

5. 联合查询(推荐)

5.1 association

bean对象

public class Lock {
    private Integer id;//锁编号
    private String LockName;//锁名
    //省略get/set/toString/构造器
}
public class Key {
    private Integer id;//钥匙编号
    private String keyName;//钥匙名
    private Lock lock;//当前钥匙能开哪把锁
    //省略get/set/toString/构造器
}

keyDao

public interface keyDao {
//将钥匙和锁信息一起查出
    public Key getKeyById(Integer id);
}

sql语句

SELECT k.id,k.`keyname`,k.`lockId`,l.`id` lid,l.`lockName` 
FROM t_key k 
LEFT JOIN t_lock l 
ON k.`lockId`=l.`id`
WHERE k.`id`=1

keyDao.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.achang.dao.keyDao">
    <select id="getKeyById" resultMap="myKey">
        select k.id,k.`keyname`,k.`lockId`,l.`id` lid,l.`lockName` from t_key k left join t_lock l on k.`lockId`=l.`id`where k.`id`=#{id}
    </select>
    <resultMap id="myKey" type="com.achang.bean.Key">
        <id column="id" property="id"/>
        <result column="keyname" property="keyName"/>
        <!--若这个对象的属性是一个对象,自定义规则;可使用association标签定义联合查询

         private Integer id;//钥匙编号
         private String keyName;//钥匙名
         private Lock lock;//当前钥匙能开哪把锁

                property属性:指定要联合查询的对象
                javaType属性:指定这个javabean属性的类型的全类名
        -->

        <association property="lock" javaType="com.achang.bean.Lock">
            <!--定义lock属性对于这个Lock对象如何封装
                property属性:指定对于javabean对象的属性
                column属性:指定数据库查询结果的字段名
            -->
            <id property="id" column="lid"></id>
            <result property="LockName" column="lockName"></result>
        </association>
    </resultMap>
</mapper>

5.2 Collection

bean对象

public class Lock {
    private Integer id;//锁编号
    private String LockName;//锁名
    private List<Key> keys//很多把钥匙可以开这把锁
    //省略get/set/toString/构造器
}
public class Key {
    private Integer id;//钥匙编号
    private String keyName;//钥匙名
    private Lock lock;//当前钥匙能开哪把锁
    //省略get/set/toString/构造器
}

sql

SELECT k.id,k.`keyname`,k.`lockId`,l.`id` lid,l.`lockName` 
FROM t_key k 
LEFT JOIN t_lock l 
ON k.`lockId`=l.`id`
WHERE k.`id`=3

LockDao.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.achang.dao.LockDao">
    <select id="getLockById" resultMap="myLock">
        select k.kid,k.`keyname`,k.`lockId`,l.`id` id,l.`lockName` from t_key k left join t_lock l on k.`lockId`=l.`id`where k.`id`=#{id}
    </select>
    
    <resultMap id="myLock" type="com.achang.bean.Lock">
        <id column="id" property="id"/>
        <result column="keyname" property="keyName"/>
        <!-- 
		collection:定义集合元素的封装
			property:指定bean对象属性中哪个集合对象
			ofType:指定bean对象属性的对象集合里面元素的类型
		-->
        <collection property="keys" ofType="com.achang.bean.Key">
            <id property="id" column="kid"></id>
            <result property="keyName" column="keyname"></result>
        </collection>
    </resultMap>
</mapper>

test

public class EmployeeDaoTest {
    SqlSessionFactory factory;
    @Before
    public void initSqlSessionFactory() throws IOException {
        String resource = "mybatis_config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        factory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    //LockDao测试
    @Test
    public void test7(){
        SqlSession sqlSession = factory.openSession();
        keyDao mapper = sqlSession.getMapper(keyDao.class);
        Lock lockById = mapper.getLockById(3);
        System.out.println(keyById);
        //lock对象为null是因为没自定义配置association;需要则可以在resultMap中继续配置association来继续套娃
        //Key{id=3, keyName='303号钥匙1', lock=null}
       // Key{id=4, keyName='303号钥匙2', lock=null}
       // Key{id=5, keyName='303号钥匙3', lock=null}
    }
}

6. 分步查询(不推荐)

虽然sql简单,但是会有大量性能浪费,虽然可以用过设置来减少性能浪费,但还是不推荐使用!!!

1. association-分步查询

select:调用目标的方法查询当前属性的值【方法全类名】

column:将指定字段值传入select属性调用的目标方法中

img

2. collection-分步查询

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IPuT2ekF-1608180164256)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201217123600895.png)]

3. 延迟加载

开启延迟加载和属性按需加载

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IAq4pypy-1608180164250)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201217123458781.png)]

7. 动态SQL

7.1 if 标签

因为在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.achang.dao.TeacherDao">
    <resultMap id="teacherMap" type="com.achang.bean.Teacher">
        <id property="id" column="id"/>
        <result property="name" column="teacherName"/>
        <result property="course" column="class_name"/>
        <result property="address" column="address "/>
        <result property="birth" column="birth_date"/>
    </resultMap>
    
    <select id="getTeacherByCondition" resultMap="teacherMap">
        SELECT * FROM t_teacher WHERE
        <!--test属性:指定编写判断条件
            id!=null:取出传入的javabean属性中id的值,判断其是否为空
        -->
        <if test="id!=null">
            id > #{id} and
        </if>
        <if test="name!=null and !name.equals(&quot;&quot;) ">
            teacherName like #{name} and
        </if>
        <if test="birth!=null">
            birth_date &lt; #{birth}
        </if>
    </select>
</mapper>

7.2 where标签

我们的查询语句就放在where标签内,每一个and放在前面

where标签:可以帮我们去除掉前面的and

<?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.achang.dao.TeacherDao">
    <resultMap id="teacherMap" type="com.achang.bean.Teacher">
        <id property="id" column="id"/>
        <result property="name" column="teacherName"/>
        <result property="course" column="class_name"/>
        <result property="address" column="address "/>
        <result property="birth" column="birth_date"/>
    </resultMap>

    <select id="getTeacherByCondition" resultMap="teacherMap">
        SELECT * FROM t_teacher
        <!--where标签:可以帮我们去除掉前面的and-->
        <where>
            <if test="id!=null">
                id > #{id}
            </if>
            <if test="name!=null and !name.equals(&quot;&quot;) ">
                and teacherName like #{name}
            </if>
            <if test="birth!=null">
                and birth_date &lt; #{birth}
            </if>
        </where>
    </select>
</mapper>

7.3 trim标签

<?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.achang.dao.TeacherDao">
    <resultMap id="teacherMap" type="com.achang.bean.Teacher">
        <id property="id" column="id"/>
        <result property="name" column="teacherName"/>
        <result property="course" column="class_name"/>
        <result property="address" column="address "/>
        <result property="birth" column="birth_date"/>
    </resultMap>
    
    <select id="getTeacherByCondition" resultMap="teacherMap">
        SELECT * FROM t_teacher
        <!--截取字符串
        prefix属性:为下面的sql整体添加一个前缀
        prefixOverrides属性:取出整体字符串钱多多余的字符
        suffix:为整体添加一个后缀
        suffixOverrides:后面哪个多了可以去掉
        -->
        <trim prefix="where" prefixOverrides="and">
            <if test="id!=null">
                id > #{id}
            </if>
            <if test="name!=null and !name.equals(&quot;&quot;) ">
                and teacherName like #{name}
            </if>
            <if test="birth!=null">
                and birth_date &lt; #{birth}
            </if>
        </trim>
    </select>
</mapper>

7.4 foreach标签

TeacherDao

public interface TeacherDao {
    public List<Teacher> getTeacherByIdIn(@Param("ids") List<Integer> ids);
}

TeacherDao.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.achang.dao.TeacherDao">
    <resultMap id="teacherMap" type="com.achang.bean.Teacher">
        <id property="id" column="id"/>
        <result property="name" column="teacherName"/>
        <result property="course" column="class_name"/>
        <result property="address" column="address "/>
        <result property="birth" column="birth_date"/>
    </resultMap>

    <select id="getTeacherByIdIn" resultMap="teacherMap">
        SELECT * FROM t_teacher WHERE id IN
        <!--foreach遍历集合
            collection属性:指定要遍历集合的key
            close属性:以书什么结束
            item属性:每次遍历的元素,命名任意,方便引用
            index属性:
                如果遍历的是一个list,指定变量保存的当前元素的索引
                    item为值
                如果遍历的是一个map,指定变量保存的当前元素的key
                    item为value值
            open属性:以什么开始
            separator属性:每次遍历元素的分隔符
        -->
        <foreach collection="ids" close=")" item="id_item" open="(" separator=",">
            #{id_item}
        </foreach>
    </select>
</mapper>

7.5 choose标签

when标签:设置情况;满足后其他情况跳过

otherwise标签:当所有情况都不满足时,就执行此标签

类似ifelse-else

<?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.achang.dao.TeacherDao">
    <resultMap id="teacherMap" type="com.achang.bean.Teacher">
        <id property="id" column="id"/>
        <result property="name" column="teacherName"/>
        <result property="course" column="class_name"/>
        <result property="address" column="address "/>
        <result property="birth" column="birth_date"/>
    </resultMap>

    <select id="getTeacherByConditionChoose" resultMap="teacherMap">
        select * from t_teacher
        <where>
            <choose>
                <when test="id!=null">
                    id=#{id}
                </when>
                <when test="name!=null and name.equals(&quot;&quot;)">
                    teacherName=#{name}
                </when>
                <when test="birth!=null">
                    birth_date=#{birth}
                </when>
                <otherwise>
                    1=1
                </otherwise>
            </choose>
        </where>
    </select>
</mapper>

7.6 set标签

用于SQL语句中的update操作

sql

UPDATE t_teacher SET 
teacherName=?,
class_name=?,
address=?,
birth_date=?
WHERE id=?

TeaherDao.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.achang.dao.TeacherDao">
    <resultMap id="teacherMap" type="com.achang.bean.Teacher">
        <id property="id" column="id"/>
        <result property="name" column="teacherName"/>
        <result property="course" column="class_name"/>
        <result property="address" column="address "/>
        <result property="birth" column="birth_date"/>
    </resultMap>

    <update id="updateTeacher">
        UPDATE t_teacher
            <set>
                <if test="name!=null and !name.equals(&quot;&quot;)">
                    teacherName=#{name},
                </if>
                <if test="course!=null and !course.equals(&quot;&quot;)">
                    class_name=#{course},
                </if>
                <if test="address!=null and !address.equals(&quot;&quot;)">
                    address=#{address},
                </if>
                <if test="birth!=null">
                    birth_date=#{birth}
                </if>
            </set>
            <where>
                id=#{id}
            </where>
    </update>
</mapper>

8. 缓存机制

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

缓存:暂时的存储一些数据;加快系统的查询速度

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

一级缓存:线程级别的缓存;本地缓冲;SqlSession级别的缓存

二级缓存:全局范围的缓存;除过当前缓冲;SqlSession能用以外其他也而已使用

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

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

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

8.1 一级缓存

只要之前查询过的数据,mybatis就会保存在一个缓存中(Map);下次获取直接从缓冲中拿

一级缓存(local cache), 即本地缓存, 作用域默认为sqlSession。当 Session flush 或 close 后, 该 Session 中的所有 Cache 将被清空。

本地缓存不能被关闭, 但可以调用 clearCache() 来清空本地缓存, 或者改变缓存的作用域.

一级缓存体验 只发送给数据库一次数据查询,且两次获取到的数据内存地址相同

public class DaoTest {
    SqlSessionFactory factory;
    @Before
    public void initSqlSessionFactory() throws IOException {
        String resource = "mybatis_config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        factory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    //体会一级缓存
    @Test
    public void test1(){
        SqlSession sqlSession = factory.openSession();
        Teacher teacherById;
        try {
            TeacherDao mapper = sqlSession.getMapper(TeacherDao.class);
            Teacher teacher1 = mapper.getTeacherById(1);
            System.out.println(teacher1);
            System.out.println("====================");
            Teacher teacher2 = mapper.getTeacherById(1);
            System.out.println(teacher2);
            System.out.println(teacher1==teacher2);//true
        } finally {
            sqlSession.commit();
            sqlSession.close();
        }
    }
}

一级缓存失效的情况

1、不同的SqlSession,使用不同的一级缓存;他会存储在自己的SqlSession的Map中

2、同一个方法,不同的参数,由于可能之前没查询过,还会发送新的sql查询请求

3、在这个sqlsession期间执行上任何一次增删改操作,增删改操作会把缓存清空

4、手动清空一级缓存 clearCache():清空当前sqlsession的一级缓存

8.2 二级缓存

  • 二级缓存(second level cache),全局作用域缓存
  • 二级缓存在SqlSession关闭或提交之后才会生效
  • 二级缓存默认不开启,需要手动配置
  • MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口

使用步骤

开启二级缓存

在mybatis全局配置文件中下配置

<settings>
    <!--开启全局缓存-->
    <setting name="cacheEnabled" value="true"/>
</settings>

给需要使用缓存的dao功能配置

<?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.achang.dao.TeacherDao">
    <!--使用二级缓存-->
    <cache></cache>
</mapper>

8.3 缓存查询顺序

缓存的查询顺序(口诀:二一库)

每一个Dao有他自己的二级缓存,每次关闭会话后一级缓存会将数据传给当前Dao的二级缓存保存

每次查询都从他的Dao中先查询二级缓存,没有就去查询这次会话的一级缓存,没有就去数据库查询

1、一级缓存和二级缓存不会有同一个数据

2、任何时候都是先看二级缓存,再看一级缓存;如果大家都没有,就去查询数据库

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z2MQq8kK-1608293796159)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20201218123837920.png)]

8.4 缓存在dao.xml配置表标签的属性

1、全局setting的cacheEnable:
–配置二级缓存的开关。一级缓存一直是打开的。

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

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

4、sqlSession.clearCache():
–只是用来清除一级缓存。

5、当在某一个作用域 (一级缓存Session/二级缓存Namespaces) 进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。

8.5 第三方缓存整合

1、导包

ehcache-core-2.6.8.jar
mybatis-ehcache-1.0.3.jar
slf4j-api-1.7.21.jar
slf4j-log4j12-1.7.21.jar

2、ehcache要工作需要一个配置文件:

文件名叫ehcache.xml。放在类路径的根目录下

3、在sql映射文件中配置使用自定义缓存

通过type属性引入EhcacheCache的全类名

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

9. PageHelper插件

使用步骤

1、导包

pagehelper-x.x.x.jar

jsqlparser-0.9.5.jar

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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
</configuration>

3、使用PageHelper提供的方法进行分页

@Controller
public class TeacherController {
    @Autowired
    TeacherService teacherService;
    
    @RequestMapping("/getAll")
    public String getAll(@RequestParam(value = "pn",defaultValue = "1")Integer pn, Model model){
        //startPage(参数1,参数2)
        //参数1:第几页
        //参数2:一页显示几个
        PageHelper.startPage(pn,5);
        List<Teacher> list = teacherService.getAll();
        model.addAttribute("teachers",list);
        return "success";
    }
}

4、可以使用更强大的PageInfo封装返回结果

PageInfo()方法;

1、可只传要封装的分页数据集合

2、可传要封装的分页数据集合和参数2(做多页显示时,显示的几页,也就是几个数据一组)

@Controller
public class TeacherController {
    @Autowired
    TeacherService teacherService;
    
    @RequestMapping("/getAll")
    public String getAll(@RequestParam(value = "pn",defaultValue = "1")Integer pn, Model model){
        PageHelper.startPage(pn,5);
        List<Teacher> list = teacherService.getAll();
        //将查询的结果放在PageInfo()中,这个PageInfo就有非常多能用的东西
        //PageInfo(参数1,参数2);参数2传入要连续要显示的页码
        PageInfo pageInfo = new PageInfo(list,5);

        System.out.println("当前页码: "+pageInfo.getPageNum());
        System.out.println("总页码: "+pageInfo.getPages());
        System.out.println("总记录数: "+pageInfo.getTotal());
        System.out.println("当前页有几条记录: "+pageInfo.getSize());
        System.out.println("当前页的pageSize: "+pageInfo.getPageSize());
        System.out.println("前一页: "+pageInfo.getPrePage());
        System.out.println("结果: "+pageInfo.getList());//查询结果
        int[] nums = pageInfo.getNavigatepageNums();

        model.addAttribute("info",pageInfo);
        return "success";
    }
}

success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>成功</h1>
    ${teacherMsg}
    <table cellpadding="5" cellspacing="0" border="1">
        <tr>
            <th>id</th>
            <th>name</th>
            <th>course</th>
            <th>address</th>
        </tr>
        <c:forEach items="${info.list}" var="tea">
            <tr>
                <td>${tea.id}</td>
                <td>${tea.name}</td>
                <td>${tea.course}</td>
                <td>温州</td>
            </tr>
        </c:forEach>
        <tr>
            <td colspan="4">
                <a href="getAll?pn=1">首 页</a><a href="getAll?pn=${info.prePage}">上一页</a>
                <c:forEach items="${info.navigatepageNums}" var="num">
                    <!--判断是否为当前页码;是为显示【当前页码】-->
                    <c:if test="${num==info.pageNum}">
                        【${num}】
                    </c:if>
                    <!--判断是否为当前页码;不是就显示【遍历的页码】-->
                    <c:if test="${num!=info.pageNum}">
                        <a href="getAll?pn=${num}">
                                ${num}
                        </a>
                    </c:if>
                </c:forEach>
                <a href="getAll?pn=${info.nextPage}">下一页</a><a href="getAll?pn=${info.pages}">末 页</a>
            </td>
        </tr>

    </table>
</body>
</html>

img

10. SSM整合

(6条消息) Day136.SSM框架的整合使用 -SSM_阿昌爱Java-CSDN博客

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值