mybatis的一些重要配置

1、全局配置文件

<?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>
    <!--
        1、mybatis 可以使用 properties 来引入外部 properties 配置文件的内容
        resource: 引入类路径下的资源
        url: 引入网络路径或者磁盘路径下的资源
    -->
    <properties resource="dbconfig.properties"></properties>

    <!--
        2、settings 包含很多重要的设置项
                setting: 用来设置每一个设置项
                    name: 设置项名
                    value: 设置项取值
    -->
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="jdbcTypeForNull" value="NULL"/>

        <!--
            显式的指定每一个我们需要更改的配置的值,即使它是默认的。防止版本更新带来的问题
        -->
        <setting name="cacheEnabled" value="true"/>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

    <!--
        3、typeAliases: 别名处理器:可以为我们的 java 类型起别名
            别名不区分大小写
    -->
    <typeAliases>
        <!--
            1、typeAlias: 为某个 java 类型起别名
                type: 指定要起别名的类型全类名,默认别名就是类名小写:employee
                alias: 指定新的别名
        -->
        <!--<typeAlias type="com.mgy.mybatis.bean.Employee" alias="empl" />-->
        <!--
            2、package: 为某个报下的所有类批量起别名
                name: 指定包名(为当前包以及下面所有的后代的每一个类都起一个默认别名(类名小写))
        -->
        <package name="com.mgy.mybatis.bean"/>
        <!--
            3、批量起别名的情况下,使用 @Alias 注解为某个类型指定新的别名
        -->
    </typeAliases>

    <!--
        4、environments: 环境们,mybatis 可以配置多种环境,default 指定使用某种环境。可以达到快速切换环境
                environment: 配置一个具体的环境信息,必须有两个标签,id 代表当前环境的唯一标识
                    transactionManager: 事务管理器
                        type: 事务管理器的类型:JDBC(JdbcTransactionFactory)|MANAGED(ManagedTransactionFactory)
                        自定义事务管理器:实现 TransactionFactory 接口,type 指定为全类名
                    dataSource: 数据源
                        type: 数据源类型:UNPOOLED(UnpooledDataSourceFactory)
                                          |POOLED(PooledDataSourceFactory)
                                         |JNDI(JndiDataSourceFactory)
                        自定义数据源:实现 DataSourceFactory 接口,type 是全类名
    -->
    <environments default="dev_mysql">
        <environment id="dev_mysql">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}" />
                <property name="url" value="${jdbc.url}" />
                <property name="username" value="${jdbc.username}" />
                <property name="password" value="${jdbc.password}" />
            </dataSource>
        </environment>

        <environment id="dev_oracle">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${orcl.driver}" />
                <property name="url" value="${orcl.url}" />
                <property name="username" value="${orcl.username}" />
                <property name="password" value="${orcl.password}" />
            </dataSource>
        </environment>
    </environments>

    <!--
        5、databaseIdProvider: 支持多数据库厂商的
            type="DB_VENDOR": VendorDatabaseIdProvider
                作用就是得到数据库厂商的标识(驱动 getDatabaseProductName())
                MySQL、Oracle、SQL Server ...
    -->
    <databaseIdProvider type="DB_VENDOR">
        <!-- 为不同的数据库厂商起别名 -->
        <property name="MySQL" value="mysql"/>
        <property name="Oracle" value="oracle"/>
        <property name="SQL Server" value="sqlserver"/>
    </databaseIdProvider>

    <!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
    <!--
        6、mappers: 将 sql 映射注册到全局配置中
    -->
    <mappers>
        <!--
            mapper: 注册一个 sql 映射
                注册配置文件
                resource: 引用类路径下的 sql 映射文件
                    mybatis/mapper/EmployeeMapper.xml
                url: 引用网络路径或磁盘路径下的 sql 映射文件
                    file:///var/mappers/AuthorMapper.xml
                注册接口
                class: 引用(注册)接口
                    1、有 sql 映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
                    2、没有 sql 映射文件,所有的 sql 都是利用注解写在接口上
                    推荐:
                        比较重要的、复杂的 Dao 接口,我们写在 sql 映射文件上
                        不重要的、简单的 Dao 接口,为了开发快速可以使用注解
        -->
<!--        <mapper resource="com.mgy.mybatis.dao/EmployeeMapper.xml" />-->
<!--        <mapper class="com.mgy.mybatis.dao.EmployeeMapper" />-->
<!--        <mapper class="com.mgy.mybatis.dao.EmployeeMapperAnnotation" />-->

        <!-- 批量注册 -->
        <package name="com.mgy.mybatis.dao"/>
    </mappers>
</configuration>

2、简单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">
<!--
    namespace: 名称空间;指定为接口的全类名
    id: 唯一标识
    resultType: 返回值类型
    #{id}: 从传递过来的参数中取出 id 值

    Employee getEmpById(Integer id);
-->
<mapper namespace="com.mgy.mybatis.dao.EmployeeMapper">

    <!--
        eviction: 缓存的回收策略
            • LRU – 最近最少使用的:移除最长时间不被使用的对象。
            • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
            • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
            • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
            • 默认的是 LRU。
        flushInterval:缓存刷新间隔
            缓存多长时间清空一次,默认不清空,设置一个毫秒值
        readOnly:是否只读
            true:只读 ==> mybatis 认为所有从缓存中获取数据的操作都是只读操作,不会修改数据
                           mybatis 为了加快获取速度,直接就会将数据在缓存中的引用交给用户
            false: 非只读 ==> mybatis 觉得获取的数据可能会被修改
                              mybatis 会利用序列化 & 反序列化的技术克隆一份新的数据给你
        size:缓存存放多少元素
        type: 指定自定义缓存的全类名
              实现 Cache 接口即可
    -->
    <!--<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache>-->

    <!-- 使用第三方缓存 -->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>

    <!--
        @MapKey("lastName")
        Map<String, Employee> getEmpByLastNameLikeReturnMap(String lastName);
    -->
    <select id="getEmpByLastNameLikeReturnMap" resultType="com.mgy.mybatis.bean.Employee">
        select * from tbl_employee where last_name like #{lastName}
    </select>

    <!--返回一条记录的 map,key 就是列名,值就是对应的值-->
    <!--Map<String, Object> getEmpByIdReturnMap(Integer id);-->
    <select id="getEmpByIdReturnMap" resultType="map">
        select * from tbl_employee where id = #{id}
    </select>

    <!--List<Employee> getEmpByLastNameLike(String lastName);-->
    <!-- resultType: 如果返回的是一个集合,要写集合中元素的类型 -->
    <select id="getEmpByLastNameLike" resultType="com.mgy.mybatis.bean.Employee">
        select * from tbl_employee where last_name like #{lastName}
    </select>

    <!--Employee getEmpByMap(Map<String, Object> map);-->
    <select id="getEmpByMap" resultType="com.mgy.mybatis.bean.Employee">
        select * from ${tableName} where id = ${id} and last_name = #{lastName}
    </select>

    <!--Employee getEmpByIdAndLastName(Integer id, String lastName);-->
    <select id="getEmpByIdAndLastName" resultType="com.mgy.mybatis.bean.Employee">
        select * from tbl_employee where id = #{id} and last_name = #{lastName}
    </select>

    <select id="getEmpById" resultType="com.mgy.mybatis.bean.Employee">
        select * from tbl_employee where id = #{id}
    </select>
    <select id="getEmpById" resultType="com.mgy.mybatis.bean.Employee" databaseId="mysql" useCache="true">
        select * from tbl_employee where id = #{id}
    </select>
    <select id="getEmpById" resultType="com.mgy.mybatis.bean.Employee" databaseId="oracle" >
        select EMPLOYEE_ID id, LAST_NAME lastName, EMAIL email from tbl_employee where id = #{id}
    </select>

    <!--Long addEmp(Employee employee);-->
    <!--
        parameterType:参数类型,可以省略
        获取自增主键的值:
            mysql 支持自增主键,自增主键值的获取,mybatis 也是利用 statement.getGeneratedKeys();
            useGeneratedKeys="true"; 使用自增主键获取主键值策略
            keyProperty: 指定对应的主键属性,也就是 mybatis 获取到主键值以后,将这个值封装给 javaBean 的那个属性
    -->
    <insert id="addEmp" parameterType="com.mgy.mybatis.bean.Employee"
        useGeneratedKeys="true" keyProperty="id" databaseId="mysql" flushCache="true">
        insert into tbl_employee(last_name, email, gender)
        values (#{lastName}, #{email}, #{gender})
    </insert>

    <!--
        获取非自增主键的值:
            Oracle 不支持自增:Oracle 使用序列来模拟自增;
            每次插入的数据的主键是从序列中拿到的值;如何获取到这个值
    -->
    <insert id="addEmp" databaseId="oracle">
        <!--
            keyProperty: 查出的主键值封装给 javaBean 的哪个属性
            order="BEFORE": 当前 sql 在插入 sql 之前运行
                   AFTER: 当前 sql 在插入之后运行
            resultType:查出的数据的返回值类型

            BEFORE 运行顺序:
                先运行 selectKey 查询 id 的 sql,查出 id 值封装给 JavaBean 的 id 属性
                再运行插入的 sql,就可以取出 id 属性对应的值
            AFTER 运行顺序:
                先运行插入的 sql(从序列中取出新值作为 id)
                再运行 selectKey 查询 id 的 sql
        -->
        <selectKey keyProperty="id" order="BEFORE" resultType="Integer">
            <!-- 编写查询主键的 sql 语句 -->
            <!-- BEFORE -->
            select EMPLOYEES_SEQ.nextval from dual
            <!-- AFTER:
             select EMPLOYEES_SEQ.currval from dual
            -->
        </selectKey>

        <!-- 插入时的主键是从序列中拿到的 -->
        <!-- BEFORE: -->
        insert into employees(employee_id, last_name, email, gender)
        values (#{id}, #{lastName}, #{email<!--, jdbcType=NULL-->}, #{gender})
        <!--
            AFTER:
            insert into employees(employees_id, last_name, email, gender)
            values(employees_seq.nextval, #{lastName}, #{email}, #{gender})
        -->
    </insert>

    <!--boolean updateEmp(Employee employee);-->
    <update id="updateEmp">
        update tbl_employee
        set last_name = #{lastName}, email = #{email}, gender = #{gender}
        where id = #{id}
    </update>

    <!--void deleteEmpById(Integer id);-->
    <delete id="deleteEmpById">
        delete from tbl_employee where id = #{id}
    </delete>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mgy.mybatis.dao.DepartmentMapper">

    <!-- 引用缓存:namespace ==> 指定和哪个名称空间下的缓存一样 -->
    <cache-ref namespace="com.mgy.mybatis.dao.EmployeeMapper"/>

    <!--Department getDeptById(Integer id);-->
    <select id="getDeptById" resultType="com.mgy.mybatis.bean.Department">
        select id, dept_name departmentName from tbl_dept where id = #{id};
    </select>

    <!--
        public class Department {
            private Integer id;
            private String departmentName;
            private List<Employee> emps;

          did  dept_name  ||  eid  last_name  email   gender
    -->
    <!--
        嵌套结果集的方式,使用 collection 标签定义关联的集合类型的属性封装规则
    -->
    <resultMap id="MyDept" type="com.mgy.mybatis.bean.Department">
        <id column="did" property="id" />
        <result column="dept_name" property="departmentName" />

        <!--
            collection: 定义关联集合类型的属性的封装规则
            ofType: 指定集合里面元素的类型
        -->
        <collection property="emps" ofType="com.mgy.mybatis.bean.Employee">
            <!-- 定义这个集合中元素的封装规则 -->
            <id column="eid" property="id" />
            <result column="last_name" property="lastName" />
            <result column="email" property="email" />
            <result column="gender" property="email" />
        </collection>
    </resultMap>

    <!--Department getDeptByIdPlus(Integer id);-->
    <select id="getDeptByIdPlus" resultMap="MyDept">
        select d.id did, d.dept_name dept_name,
               e.id eid, e.last_name last_name, e.email email, e.gender gender
        from tbl_dept d
        left join tbl_employee e
        on d.id = e.d_id
        where d.id = #{id}
    </select>

    <!-- collection: 分段查询 -->
    <resultMap id="MyDeptStep" type="com.mgy.mybatis.bean.Department">
        <id column="id" property="id" />
        <result column="dept_name" property="departmentName" />
        <collection property="emps"
                    select="com.mgy.mybatis.dao.EmployeeMapperPlus.getEmpsByDeptId"
                    column="{deptId=id}" fetchType="eager">
        </collection>
    </resultMap>

    <!--Department getDeptByIdStep(Integer id);-->
    <select id="getDeptByIdStep" resultMap="MyDeptStep">
        select id, dept_name from tbl_dept where id = #{id}
    </select>

    <!--
        扩展:多列的值传递过去:
                将多列的值封装在 map 中传递过去:
                    column="{key1=column1, key2=column2}"
              fetchType="lazy": 表示使用延时加载
                   - lazy:延时加载
                   - eager: 立即加载
    -->
</mapper>

3、mybatis的逆向工程

  ① 添加依赖

<!-- mybatis 的代码生成器 -->
<dependency>
   <groupId>org.mybatis.generator</groupId>
   <artifactId>mybatis-generator-core</artifactId>
   <version>1.3.2</version>
</dependency>
<!-- mybatis-generator自动生成代码插件 -->
<dependency>
   <groupId>org.mybatis.generator</groupId>
   <artifactId>mybatis-generator-maven-plugin</artifactId>
   <version>1.3.2</version>
</dependency>

  ② 生成代码的配置文件 mbg.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>

    <!--
        targetRuntime="MyBatis3Simple":生成简单版的CRUD
        MyBatis3:豪华版
     -->
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3306/mybatis?allowMultiQueries = true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;userSSL=false&amp;serverTimezone=GMT%2B8"
                        userId="root"
                        password="root">
        </jdbcConnection>

        <!-- java 类型处理器 -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>

        <!--
            javaModelGenerator:指定 javaBean 的生成策略
            targetPackage:目标包名
            targetProject:目标工程
        -->
        <javaModelGenerator targetPackage="com.mgy.mybatis.bean" targetProject=".srcmainjava">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>

        <!-- sqlMapGenerator: sql 映射生成策略 -->
        <sqlMapGenerator targetPackage="com.mgy.mybatis.dao" targetProject=".srcmainjava">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>

        <!-- javaClientGenerator: 指定 mapper 接口所在的位置 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.mgy.mybatis.dao" targetProject=".srcmainjava">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <!-- 指定要逆向分析哪些表:根据表要创建 javaBean -->
        <table tableName="tbl_dept" domainObjectName="Department"></table>
        <table tableName="tbl_employee" domainObjectName="Employee"></table>
    </context>
</generatorConfiguration>

 4、mybatis整合spring的配置文件

<?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:context="http://www.springframework.org/schema/context"
    xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <!-- Spring希望管理所有的业务逻辑组件,等。。。 -->
    <context:component-scan base-package="com.atguigu.mybatis">
        <context:exclude-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <!-- 引入数据库的配置文件 -->
    <context:property-placeholder location="classpath:dbconfig.properties" />
    <!-- Spring用来控制业务逻辑。数据源、事务控制、aop -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!-- spring事务管理 -->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 开启基于注解的事务 -->
    <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
    
    <!-- 
    整合mybatis 
        目的:1、spring管理所有组件。mapper的实现类。
                service==>Dao   @Autowired:自动注入mapper;
            2、spring用来管理事务,spring声明式事务
    -->
    <!--创建出SqlSessionFactory对象  -->
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <!-- configLocation指定全局配置文件的位置 -->
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <!--mapperLocations: 指定mapper文件的位置-->
        <property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"></property>
    </bean>
    
    <!--配置一个可以进行批量执行的sqlSession  -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg>
        <constructor-arg name="executorType" value="BATCH"></constructor-arg>
    </bean>
    
    <!-- 扫描所有的mapper接口的实现,让这些mapper能够自动注入;
    base-package:指定mapper接口的包名
     -->
    <mybatis-spring:scan base-package="com.atguigu.mybatis.dao"/>
    <!-- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.atguigu.mybatis.dao"></property>
    </bean> -->
    
</beans>

 5、oracle 存储过程的使用

create or replace procedure 
    hello_test(
        p_start in int, p_end in int, p_count out int, p_emps out sys_refcursor
    ) as
begin
    select count(*) into p_count from employees;
    open p_emps for
        select * from (select rownum rn, e.* from employees e where rownum <= p_end)
            where rn >= p_start;
end hello_test;
<!-- public void getPageByProcedure(); 
    1、使用select标签定义调用存储过程
    2、statementType="CALLABLE":表示要调用存储过程
    3、{call procedure_name(params)}
    -->
    <select id="getPageByProcedure" statementType="CALLABLE" databaseId="oracle">
        {call hello_test(
            #{start,mode=IN,jdbcType=INTEGER},
            #{end,mode=IN,jdbcType=INTEGER},
            #{count,mode=OUT,jdbcType=INTEGER},
            #{emps,mode=OUT,jdbcType=CURSOR,javaType=ResultSet,resultMap=PageEmp}
        )}
    </select>
**
 * 封装分页查询数据
 * @author lfy
 *
 */
public class OraclePage {
    
    private int start;
    private int end;
    private int count;
    private List<Employee> emps;
        // setter and getter
}
/**
     * oracle分页:
     *         借助rownum:行号;子查询;
     * 存储过程包装分页逻辑
     * @throws IOException 
     */
    @Test
    public void testProcedure() throws IOException{
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        SqlSession openSession = sqlSessionFactory.openSession();
        try{
            EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
            OraclePage page = new OraclePage();
            page.setStart(1);
            page.setEnd(5);
            mapper.getPageByProcedure(page);
            
            System.out.println("总记录数:"+page.getCount());
            System.out.println("查出的数据:"+page.getEmps().size());
            System.out.println("查出的数据:"+page.getEmps());
        }finally{
            openSession.close();
        }
        
    }