Mybatis

Mybatis概述

MyBatis是一款优秀的持久层ORM(Object Relation Mapping)框架
MyBati内部封装了jdbc,使开发者只需要关注sql语句本身而不需要话费精力加载驱动,创建连接。创建statement等复杂工程,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。
所谓ORM就是一种为了解决面向对象与关系型数据库类型不匹配的技术。通过java对象与数据库表之间的映射关系自动将java应用程序中的对象持久化到关系型数据库表中。
使用ORM框架后,应用程序不再直接访问底层数据库,而是以面向对象的方式持久化对象,而ORM框架则会通过映射关系将这些面向对象的操作转换成底层的sql操作。

使用步骤

1在根目录下创建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>
    <properties resource="db.properties"/>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driverClass}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>    
</configuration>

2根据数据表创建实体类,采用驼峰命名法属性对应字段名
3写dao接口和映射文件mapper.xml

package com.lanou.dao;

import com.lanou.pojo.Employee;

public interface EmployeeMapper {

    Employee selectEmployeeById(int id);
    
}

mapper.xml中的namespace是接口的类全限定名,id是接口的方法名

<?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.lanou.dao.EmployeeMapper">
    <select id="selectEmployeeById" resultType="com.lanou.pojo.Employee">
        select <include refid="selectColumn"></include>from t_employee where id=#{id}
    </select>

</mapper>

4测试类

 @Test
 public void getEmployeeById(int id){
        SqlSession sqlSession =null;
        try {
            sqlSession=sqlSession();
            EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
            Employee employee = employeeMapper.selectEmployeeById(1);
            System.out.println(employee);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            sqlSession.close();
        }
    }
 private SqlSession sqlSession() throws IOException {
		String resource = "mybatis-config.xml";
 		InputStream is = Resources.getResourceAsStream(resource);
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession = sessionFactory.openSession();
        return sqlSession;
    }

Mybatis操作如下:
1读取配置文件,2根据配置文件构建SqlSessionFactory,3通过SqlSessionFactory创建sqlSession,4使用SqlSession对象操作数据库(查询、添加、修改、删除,提交事务)

Mybatis的两个核心对象
SqlSessionFactory和SqlSession
SqlSessionFactory对象的实例通过SqlSessionFactoryBuilder对象来构建,通过xml配置文件或预先定义好的configuration实例构建出SqlSessionFactory的实例
SqlSessionFactory对象是线程安全的,一旦被创建,整个应用执行期间都会存在

String resource = "mybatis-config.xml";
 		InputStream is = Resources.getResourceAsStream(resource);
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);

SqlSession是一个单线程对象,底层封装了JDBC连接,可以直接使用其来执行已映射的sql语句
SqlSession实例不能被共享,线程不安全,使用过后在finally块中关闭

增删改查

insert

常用属性说明
keyProperty将插入或更新操作的返回值赋给pojo类的某个属性,通常设置为主键对应的属性
useGenerateKeys获取数据库自增生成的字段,默认值为false

如果使用的数据库支持主键自动增长,那么可以通过keyProperty属性指定pojo类的某个属性接收主键返回值(通常会设置到id属性上)然后将useGeneratedKeys的属性值设置为true,配置之后执行插入操作会返回插入成功的行数和插入行的主键值
插入一条数据并返回主键

    <insert id="insertEmployeeReturnId" parameterType="com.lanou.pojo.Employee" useGeneratedKeys="true"
            keyProperty="id">
        insert into t_employee(last_name,email,gender)values (#{lastName},#{email},#{gender})
    </insert>

插入多条数据

    <insert id="insertEmployees" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">
        insert into t_employee(last_name,email,gender)values
        <foreach collection="list" item="employee" index="index" separator=",">
            (
            #{employee.lastName},
            #{employee.email},
            #{employee.gender}
            )
        </foreach>
    </insert>

select

常用属性说明
id命名空间的唯一标识符
parameterTypeSQL语句参数类的全限定名或别名,,是可选属性,mybatis可以通过TypeHandler推断出具体传入语句的参数
resultTypeSQL语句返回类型的类的全限定名或别名,如果是集合类型,那么返回的应该是集合可以包含的类型,而不是集合本身,返回时可使用resultType或resultMap之一
resultMap表示外部resultMap的命名引用,返回时可使用resultType或resultMap之一

根据主键查询数据,parameterType才用mybatis的TypeHandler自动判断

<sql>元素:定义可重用的sql代码片段,然后在其他语句中 <include refid="selectColumn"/>引用
  <sql id="selectColumn">
        id,last_name lastName, email,gender 
    </sql>
    <!--根据主键查数据-->
    <select id="selectEmployeeById" resultType="com.lanou.pojo.Employee">
        select <include refid="selectColumn"></include>from t_employee where id=#{id}
 </select>

多个参数查询数据,采用@Param("")起别名


    <select id="selectEmployeeByNameAndGender" resultType="com.lanou.pojo.Employee">
            select id,last_name lastName, email,gender from t_employee where last_name=#{name} and gender=#{gender}
    </select>

查询多个数据,返回时可使用resultType或resultMap之一

resultMap元素的属性、子元素功能
type属性需要映射的pojo
id属性是resultMap的唯一标识,
子元素constructor用于配置构造方法(当一个pojo未定义无参的构造方法时,使用该元素进行配置)
子元素id用于表示哪个列名是主键
子元素result用于表示pojo和数据表中的普通列的映射关系
子元素 association和collection用于处理多表时的关联关系
 <resultMap id="empMap" type="com.lanou.pojo.Employee">
     <id column="id" property="id"/>
     <result column="last_name" property="lastName"/>
     <result column="email" property="email"/>
     <result column="gender" property="gender"/>
 </resultMap>
 <select id="selectAllEmployee" resultMap="empMap">
     select id,last_name lastName,email,gender from t_employee
 </select>

 <select id="selectEmployeeReturnMap" resultType="com.lanou.pojo.Employee">
             select id,last_name lastName,email,gender from t_employee
 </select>

association
关联关系:一对一

association的属性作用
property指定映射到的实体类对象属性,与表字段一对一对应
column指定表中对应的字段
javaType指定映射到实体对象属性的类型
select指定引入嵌套查询的字SQL语句,用于关联映射中的嵌套查询
fetchType指定在关联查询时是否启用延迟加载,lazy和eager两个属性,默认是lazy
 <resultMap id="empMap1" type="com.lanou.pojo.Employee">
        <id column="id" property="id"/>
        <result column="last_name" property="lastName"/>
        <result column="email" property="email"/>
        <result column="gender" property="gender"/>
        <association property="dept" javaType="com.lanou.pojo.Dept">
            <id column="did" property="id"/>
            <result column="dept_name" property="deptName"/>
        </association>
    </resultMap>

    <select id="getEmpAndDept" resultMap="empMap1">
        SELECT e.id,e.last_name,e.email,e.gender,d.id did,d.dept_name  FROM t_dept d LEFT JOIN t_employee e on
d.id=e.d_id
    </select>

collection
关联关系:一对多
collection大部分属性与association相同,但包含一个特殊属性ofType,该属性与javaType属性对应,用于指定实体对象中集合类属性所包含的元素类型

 <resultMap id="dept1" type="com.lanou.pojo.Dept">
        <id column="eid" property="id"/>
        <result column="dept_name" property="deptName"/>
        <collection property="employees" javaType="java.util.ArrayList" ofType="com.lanou.pojo.Employee">
            <id column="id" property="id"/>
            <result column="last_name" property="lastName"/>
            <result column="email" property="email"/>
            <result column="gender" property="gender"/>
        </collection>
    </resultMap>
    <select id="getAllEmp" resultMap="dept1">
        SELECT d.id,dept_name,e.id eid,e.last_name,e.email,e.gender FROM t_dept d  JOIN t_employee e on
d.id=e.d_id
    </select>

Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载, association 指的就是一对一, collection 指的就是一对多查询。在 Mybatis 配置文件中, 可以配置是否启用延迟加载 lazyLoadingEnabled=true|false。

它的原理是, 使用 CGLIB 创建目标对象的代理对象, 当调用目标方法时, 进入拦截器方法, 比如调用 a.getB().getName(), 拦截器 invoke()方法发现 a.getB()是null 值, 那么就会单独发送事先保存好的查询关联 B 对象的 sql, 把 B 查询上来, 然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成 a.getB().getName()方法的调用。这就是延迟加载的基本原理。


update

使用set和if元素组装update语句,让程序只更新需要更新的字段

<update id="updateEmpById" parameterType="com.lanou.pojo.Employee">
        update t_employee
        <set>
            <if test="employee.lastName!=null and employee.lastName!=''">
                last_name=#{employee.lastName}
            </if>
            <if test="employee.email!=null and employee.email!=''">
                email =#{employee.email}
            </if>
            <if test="employee.gender !=null and employee.gender!=''">
                gender=#{employee.gender}
            </if>
        </set>
        where id=#{id}
    </update>

delete

<delete id="deleteEmployeeById" parameterType="int">
        delete from t_employee where id=#{id}
 </delete>

mybatis动态SQL

Mybatis 动态 sql 可以在 Xml 映射文件内,以标签的形式编写动态 sql,执行原理是根据表达式的值 完成逻辑判断并动态拼接 sql 的功能。

Mybatis 提供了 9 种动态 sql 标签:trim | where | set | foreach | if | choose| when | otherwise | bind 。

标签作用
if用于单条件分支判断
choose (when otherwise)用于多条件分支判断
where、trim、set辅助元素,用于处理一些SQL拼装,特殊字符问题
foreach循环语句,用于in语句等列举条件
bind从OGNL表达式中创建一个变量,将其绑定到上下文,用于模糊查询的sql中

即使where之后的内容有多余的and where元素会自动将他们去除。

 <select id="getByCondition" resultMap="empMap">
        <bind name="_lastName" value="'%'+lastName+'%'"/>
        select <include refid="selectColumn"></include>from  t_employee

        <where>
            <if test="id!=null">
                id=#{id}
            </if>
            <if test="lastName!=null and lastName!=''">
                and last_name  like #{_lastName}
            </if>
            <if test="email!=null and email !=''">
                and  email=#{email}
            </if>
            <if test="gender==0 or gender==1">
                and gender=#{gender}
            </if>
        </where>
    </select>

使用trim做到和where相同的功能:
prefix属性代表的是语句的前缀,prefixOverrides属性代表需要去除的那些特殊字符串

<select id="getByCondition" resultMap="empMap">
        <bind name="_lastName" value="'%'+lastName+'%'"/>
        select <include refid="selectColumn"></include>from  t_employee

        <trim prefix="where" prefixOverrides="and">
            <if test="id!=null">
                id=#{id}
            </if>
            <if test="lastName!=null and lastName!=''">
                and last_name  like #{_lastName}
            </if>
            <if test="email!=null and email !=''">
                and  email=#{email}
            </if>
            <if test="gender==0 or gender==1">
                and gender=#{gender}
            </if>
        </trim>
    </select>

foreach
item:配置的是循环中当前的元素
collection:配置的是传来的参数类型(首字母小写)可以是array、list等
open,close:配置的是以什么符号将这些集合元素包装起来
separator:配置的是各个元素的间隔符

 <delete id="deleteEmployeeByIds" parameterType="java.util.Arrays">
        delete  from t_employee where id in
        <foreach collection="array" item="idArray" separator="," open="(" close=")">
            #{idArray}
        </foreach>
    </delete>

collection的3种情况:

  • 如果传入的是单参数且参数类型是一个数组或者List时,collection属性分别是:array、list
  • 如果传入的参数有多个,就需要把它们封装成一个Map,这时collection的属性值就是Map的键
  • 如果传入的参数pojo包装类,collection属性值就是该包装类中需要进行遍历的数组或集合的属性名

分页

导入jar包
在配置文件中配置

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

使用
pageHelper拦截器在查询之前将数据分页

 private void getAllEmployee(){
        SqlSession sqlSession =null;
        try {
            sqlSession=sqlSession();
            EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
            Page<Object> page = PageHelper.startPage(1, 5);
            List<Employee> employees = employeeMapper.selectAllEmployee();
            for (Employee employee : employees) {
                System.out.println(employee);
            }
            System.out.println("当前页码"+page.getPageNum());
            System.out.println("总记录数"+page.getTotal());
            System.out.println("每页的记录数"+page.getPageSize());
            System.out.println("总页码"+page.getPages());

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            sqlSession.close();
        }
    }

mybatis缓存

mybatis有一级缓存(本地缓存)和二级缓存(全局缓存),一级缓存是一直开启的,二级缓存需要配置

  1. 开启全局二级缓存配置
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>
  1. 去mapper.xml中配置使用二级缓存:
<cache></cache>
  1. POJO需要实现序列化接口

第三方缓存整合:

  • 1)、导入第三方缓存包即可;
  • 2)、导入与第三方缓存整合的适配包;官方有;
  • 3)、mapper.xml中使用自定义缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>

mybatis四大组件

  • Executor
  • StatementHandler
  • ParameterHandler
  • ResultSetHandler

拦截器和过滤器的区别

过滤器 Filter,是 JavaEE 的标准,依赖于 Servlet 容器,使用的时候是配置在 web.xml 文件中的,可以配置多个,执行的顺序是根据配置顺序从上到下。常用来配置请求编码以及过滤一些非法参数,垃圾信息或者是网站登录验证码。
1)过滤器(Filter):它依赖于servlet容器。在实现上,基于函数回调,它可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的,是用来做一些过滤操作,获取我们想要获取的数据,比如:在Javaweb中,对传入的request、response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者Controller进行业务逻辑操作。通常用的场景是:在过滤器中修改字符编码(CharacterEncodingFilter)、在过滤器中修改HttpServletRequest的一些参数(XSSFilter(自定义过滤器)),如:过滤低俗文字、危险字符等。

<filter>
    <filter-name>encoding</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>encoding</filter-name>
    <servlet-name>/*</servlet-name>
</filter-mapping>

2、拦截器(Interceptor)
拦截器的配置一般在SpringMVC的配置文件中,使用Interceptors标签,具体配置如下:

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**" />
        <bean class="com.scorpios.atcrowdfunding.web.LoginInterceptor"></bean>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/**" />
        <bean class="com.scorpios.atcrowdfunding.web.AuthInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>

(2)拦截器(Interceptor):它依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用,就是在service或者一个方法前,调用一个方法,或者在方法后,调用一个方法,比如动态代理就是拦截器的简单实现,在调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在调用方法后打印出字符串,甚至在抛出异常的时候做业务逻辑的操作。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。

过滤器的运行是依赖于servlet容器,跟springmvc等框架并没有关系。并且,多个过滤器的执行顺序跟xml文件中定义的先后关系有关。
两者的本质区别:拦截器(Interceptor)是基于Java的反射机制,而过滤器(Filter)是基于函数回调。从灵活性上说拦截器功能更强大些,Filter能做的事情,都能做,而且可以在请求前,请求后执行,比较灵活。Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录不登录之类),太细的话,还是建议用interceptor。不过还是根据不同情况选择合适的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值