从来就没有救世主  也不靠神仙皇帝  要创造人类的幸福  全靠我们自己  

2.mybatis

 

1. 组件

  

  1.1 SqlSessionFactoryBuilder

  利用xml或java编码(Configuration)构建SqlSessionFactory

   

   

 

2. 生命周期

(1)SqlSessionFactoryBuilder

  作为SqlSessionFactory的构建者,当SqlSessionFactory创建完成,则此对象就没用了。因此它的生命周期应只存在于 方法的局部。

 

(2)SqlSessionFactory

  作用是创建SqlSession(一个会话,类似JDBC的Connection对象),每次要访问数据库时,需要创建一个SqlSession,所以此工厂类对象应存在mybatis应用的整个生命周期中。

  它的作用单一,就是创建SqlSession,因此采用单例模式(一个数据库对应一个SqlSessionFactory)

 

(3)SqlSession

  在数据库处理事务过程中有效存在

  线程不安全

  使用完成后,需要close归还给连接池

 

(4)Mapper

  作用是发送SQL,返回需要的结果。

  存活在一个SqlSession事务方法之内,当SelSession销毁时,Mapper也会销毁

 

 

3. 一个utils工具类

  MybatisUtil.java

package com.xt.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MybatisUtil {

    //单例工厂对象
    private static SqlSessionFactory sqlSessionFactory = null;

    //类线程锁
    private static final Class CLASS_LOCK = MybatisUtil.class;

    /*
    * 私有构造 单例
    * */
    private MybatisUtil() {}

    /*
    * 构建工厂
    * */
    public static SqlSessionFactory initSqlSessionFactory() {
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }

        synchronized (CLASS_LOCK) {
            if(sqlSessionFactory == null) {
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            }
        }

        return sqlSessionFactory;
    }

    /*
    * 获取一个SqlSession
    * */
    public static SqlSession getSqlSession() {
        if(sqlSessionFactory == null) {
            initSqlSessionFactory();
        }
        return sqlSessionFactory.openSession();
    }
}

 

 

 

4. 配置

  基本配置格式:

<?xml version="1.0" encoding="UTF-8"?>

<configuration>
    <properties/>     <!--属性-->  
    <settings/>       <!--设置-->
    <typeAliases/>    <!--类型命名-->
    <typeHandlers>    <!--类型处理器-->
    <objectFactory/>  <!--对象工厂-->
    <plugins/>        <!--插件-->
    
    <environments default="mysql">  <!-- 配置环境 -->   
        <environment id="mysql"> <!--mysql环境配置-->
            <transactionManager type="JDBC"></transactionManager>  <!--配置事务类型-->
            <!--配置数据源(连接池)-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/contacts?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="xxx"/>
            </dataSource>
        </environment>
    </environments>
    <!--指定映射配置文件(每个dao独立的配置文件)的位置-->
    <mappers>
        <mapper resource="com/xt/dao/UserDao.xml"></mapper>
<!--        <mapper class="com.xt.dao.UserDao"></mapper>-->
    </mappers>
</configuration>

 

  4.1  properties

  优先级:(1)>(2)>(3)

(1)property子元素

  在上面的properties里面设置dirver、url等:

<properties>     <!--属性-->  
    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/contacts?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=utf8"/>
    <property name="username" value="root"/>
    <property name="password" value="xxx"/>
</properties>

  在下面datasource里面取值:

<dataSource type="POOLED">
    <property name="driver" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
</dataSource>

 

(2)properties配置文件

  建一个配置文件:jdbc.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/contacts?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=utf8
username=root
password=xxx

  在mybatis的配置文件的properties标签里面引入:然后可在datasource标签里取出来

<properties resource="jdbc.properties" />

 

 

(3)程序参数传递

  配置文件里面的属性加密,decode解密?????  

  书上这样写的:修改后的工具类

public static SqlSessionFactory initSqlSessionFactory() {
    String resource = "SqlMapConfig.xml";  //mybatis主配置文件
    InputStream cfgStream = null;  //主配置文件输入流
    Reader cfgReader = null;       //给工厂建造者作为参数使用
    InputStream proStream = null;  //属性配置文件输入流
    Reader proReader = null;       //给工厂建造者作为入参使用
    Properties properties = null;  //接收属性配置文件
    try {
        //读入主配置文件
        cfgStream = Resources.getResourceAsStream(resource);
        cfgReader = new InputStreamReader(cfgStream);
        //读入属性配置文件
        proStream = Resources.getResourceAsStream("jdbc.properties");
        proReader = new InputStreamReader(proStream);
        properties = new Properties();
        properties.load(proReader);
        //属性解密
        properties.setProperty("username",decode(properties.getProperty("username")));
        properties.setProperty("passoword",decode(properties.getProperty("password")));
    } catch (IOException e) {
        e.printStackTrace();
    }

    synchronized (CLASS_LOCK) {
        if(sqlSessionFactory == null) {
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(cfgReader,properties);
        }
    }

    return sqlSessionFactory;
}

 

 

  4.2 settings

  

 

   

 

  

 

  

 

 

  4.3 typeAliases

  用简短名称代替过长的类全限定名,这个名称可在mybatis上下文中使用

(1)系统定义别名

  在 org.apache.ibatis.type.TypeAliasRegistry 定义了这些

  

 

  

 

 

(2)自定义

  自定义类的别名:使用xx代替很长的全限定名

<typeAliases>
    <typeAlias alias="xx" type="com.xt.abc.efg.Xx"/>
</typeAliases>

 

   包中有很多类时,在各个类上面加注解,然后配置里不写alias,mybatis会自己扫描各个别名:

<typeAliases>
    <package name="com.xt.abc.efg" />
    <package name="com.xt.pack2.abc" />
</typeAliases>

  包中类的信息:例

package com.xt.abc.efg

@Alias("classa")
public class ClassA {}

@Alias("classb")
public class ClassB {}

 

  4.4 typeHandler 类型处理器

  在预处理语句PreparedStatement中设置一个参数时,或从结果集ResultSet中取出一个值时,都会用注册了的 typeHandler处理

  常用:java类型、jdbc类型

  typeHandler:将参数从javaType转化为jdbcType,或从数据库取出结果时将jdbcType转化为javaType

(1)系统预定义的

  org.apache.ibatis.type.TypeHandlerRegistry 

 

(2)自定义

 

(3)枚举类型typeHandler

 

  4.5 objectFactory

  mybats在构建一个结果返回时,会使用 ObjectFactory 构建POJO

  默认的:org.apache.ibatis.reflection.factory.DefaultObjectFactory

 

  4.6 插件

 

  4.7 environments

  注册多个数据源,每个数据源包括:数据库源配置、数据库事务配置

(1)数据源

 

(2)事务

  事务由 SqlSession控制(commit、rollback)

 

  4.8 映射

 

 

5.  映射器

<?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.xt.dao.UserDao">
    <!--配置查询所有-->
    <select id="findAll" resultType="com.xt.domain.User">
        select * from user
    </select>

    <select id="findById" parameterType="int" resultType="com.xt.domain.User">
        select * from user where id = #{id}
    </select>

    <insert id="insertUser" parameterType="com.xt.domain.User" >
        insert into user values (null,#{username},STR_TO_DATE(#{birthday},'%Y-%m-%d %H:%i:%s'),#{sex},#{address})
    </insert>

    <delete id="deleteUser" parameterType="int" >
        delete from user where id = #{id}
    </delete>
</mapper>

 

  5.1 select

(1)元素配置

 

(2)自动映射

  java中属性名和数据库中字段名不一致时,可通过数据库的别名形成自动映射

  自动映射的设置在 <settings> 中配置

 

(3)多个入参

  ①map传递

  例:

    xml配置:

<select id="findByMap" parameterType="map" resultType="com.xt.domain.User">
    select * from user where username like concat('%',#{username},'%')
    and address like concat('%',#{address},'%')
</select>

    接口:

public List<User> findByMap(Map<String,String> params);

    调用者:

Map<String,String> params = new HashMap(String,String)();
params.put("username","xt");
params.put("address","address1");
mapper.findByMap(params);

 

  ②注解

    xml配置:

<select id="findByMap" resultType="com.xt.domain.User">
    select * from user where username like concat('%',#{username},'%')
    and address like concat('%',#{address},'%')
</select>

    接口:

public List<User> findByMap(@Param("username") String name,@Param("address")String addr);

    调用者直接传入两个字符串即可

 

  ③javaBean

    参数很多时,创建一个类,参数作为成员,有getter、setter,在select标签里parameterType就是该类全限定名

 

(4)resultMap映射结果集

  查询返回的结果可能不能完全映射到一个java bean上去,因此定义resultMap接收返回结果集

 

  5.2  insert、update、delete

 

  5.3 参数

(1)参数配置

  指定特定类型的处理器、指定数值类型保存的精度

  例:

#{age,javaType=int,jdbcType=NUMERIC}  //使用系统定义的typeHandler处理

 

#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}  //使用自定义的typeHandler处理

 

#{price,javaType=double,jdbcType=NUMERIC,numericScale=2}

 

(2)存储过程支持

 

(3)特殊字符替换和处理

  #   $

 

  5.4 sql元素  

  

  5.5 resultMap

 

  5.6 缓存

(1)系统缓存

  默认开启一级缓存,在参数和SQL完全一样的情况下,使用同一个SqlSession对象调用同一个Mapper的方法,一般只会执行一次SQL。但在一个SqlSession提交commit后再开一个SqlSession,如果也有缓存的话,它和之前的缓存是相互隔离的,需要重新执行SQL(即使参数、SQL还是一样)

  二级缓存可使缓存在SqlSessionFactory层面上能提供给各个SqlSession对象共享

 

  开启二级缓存要求返回的POJO能序列化(实现了Serializable接口),然后在映射xml文件里加上 <cache/>

  ①全局配置的settings里面加

<settings>
    <setting name="cacheEnabled" value="true" />
</settings>

  ②映射xml里面

<cache/>

  也可以设置缓存的属性

 

  如果某些语句需要每次调用都执行sql(即个别语句不用缓存),可在其标签内加 useCache   flushCache属性

  如:useCache默认值为true

<select id="xx" useCache="false" resultType="xxxxx" parameterType="xxx">
    sql语句
</select>

 

 

 

(2)自定义缓存

  比如用Redis作为缓存

  需要实现 org.apache.ibatis.cache.Cache 接口,实现类在 cache标签里通过type属性指定

    

---

6. 动态SQL

   6.1 if

  例:模糊查询,传入的username可能传了空,则需要判断入参值然后决定是否拼sql

<select id="findUser" parameterType="string" resultTypeMap="userResultMap">
    select user_no,user_name,note from t_user where 1 = 1
    <if test="username != null and username != ''">
        and user_name like concat('%',#{username},'%')
    </if>
</select>

 

  6.2  choose、when、otherwise

<select id="findUser" parameterType="User" resultTypeMap="userResultMap">
    select user_no,user_name,note from t_user where 1 = 1
    <choose>
        <when test="userNo != null and userNo != ''">
            AND user_no = #{userNo}
        </when>
        <when test="userName != null and userName != ''">
            AND user_name like concat('%',#{userName},'%')
        </when>
        <otherwise>
            AND note is not null
        </otherwise>
    </choose>
</select>

 

  6.3 trim、where、set

(1)where:where里面的元素的条件成立时,生成 where 关键字组成sql(这样就不用写 where 1 = 1了)

  而且,如果里面语句开头是 AND、OR,也会去掉(比如这里前面的sex为空,而username不为空,则去掉and),然后组成正确的SQL

<select id="findUser" parameterType="string" resultTypeMap="userResultMap">
    select user_no,user_name,note from t_user 
    <where>
        <if test="sex != null and sex != ''">
            sex like concat('%',#{sex},'%')
        </if>
        <if test="username != null and username != ''">
            and user_name like concat('%',#{username},'%')
        </if>
    </where>
</select>

 

(2)trim

  trim可以自定义元素的功能

  ①自定义上面的where元素:

<select id="findUser" parameterType="string" resultTypeMap="userResultMap">
    select user_no,user_name,note from t_user 
    <trim prefix="WHERE" prefixOverride="AND | OR">
        <if test="sex != null and sex != ''">
            sex like concat('%',#{sex},'%')
        </if>
        <if test="username != null and username != ''">
            and user_name like concat('%',#{username},'%')
        </if>
    </trim>
</select>

  trim里面的子元素不是空的的时候,插入 prefix(即 WHERE);如果这里第一个条件是空的而第二个不是空,则去掉 prefixOverride(即这里的and)形成正确的sql

 

  ②自定义下面的set元素

<update id="xx">
    update table1
    <trim prefix="SET" suffixOverrides=",">
        <if test="username != null and username != ''">username=#{username},</if>
        <if test="sex != null and sex != ''">sex=#{sex},</if>
        <if test="addr != null and addr != ''">address=#{addr},</if>
    </trim>
    where id = #{id}
</update>

 

(3)set

  上面的where、trim可用来动态拼接条件,而set可用在update更新语句中动态包含要更新的列

<update id="xx">
    update table1
    <set>
        <if test="username != null and username != ''">username=#{username},</if>
        <if test="sex != null and sex != ''">sex=#{sex},</if>
        <if test="addr != null and addr != ''">address=#{addr},</if>
    </set>
    where id = #{id}
</update>

 

 

 

  6.4 foreach

  如果要动态生成 如 select * from xx where field1 in (xxx,xxx,xxx) 这样的语句,调用者可传入数组、List、Set集合对象等

  例:

<select id="findByAddrList" resultType="com.xt.domain.User">
    select * from user where address in
    <foreach item="addr" index="i" collection="list" open="(" separator="," close=")">
        #{addr}
    </foreach>
</select>

  collection:传进来的参数名称

  item:循环中当前元素,即值

  index:集合下标或键

  open、close:

  separator:分隔符

 

  另一个问题:如果输入的list或set、数组里面没有数据,则会导致  select * from t where address in ()  这样的错误SQL

  修改如下:

<select id="findByAddrList" resultType="com.xt.domain.User">
    select * from user
    <where>
        <if test="list.size()>0">
            address in
        </if>
        <foreach item="addr" index="i" collection="list" open="(" separator="," close=")">
            #{addr}
        </foreach>
    </where>
</select>

 

  6.5  bind

  在OGNL表达式外创建一个变量,并将其绑定到当前的上下文

  作用就是简化写法。而且bind是将param视作字符串拼接,能防止注入。

<select id="xx" resultType="xxx">
    <bind name="pattern" value="'%' + param + '%'"/>
    select * from table1 where name like #{pattern}  --在上面定义的pattern在这里就用到了
</select>

 

posted @ 2020-06-30 07:18  T,X  阅读(155)  评论(0编辑  收藏  举报