博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
mybatis 学习的总结
阅读量:4332 次
发布时间:2019-06-06

本文共 18286 字,大约阅读时间需要 60 分钟。

什么是mybatis?

他是java基于持久层的框架(和数据库交互的一层),它支持定制化 SQL、存储过程以及高级映射(不像hibernate把发送sql作为暗箱操作,而他是可以完全看出来的可以看的到的sql语句).MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

mybatis有什么用?

定制化sql,存储过程以及高级映射。

mybatis怎么使用?

重要对象

SqlSessionFactory

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。

怎么构建一个SqlSessionFacrory?

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

SqlSession

sqlsession表示与数据库之间的会话,用完必须关闭,与conection一样都是非线程安全(使用非线程安全对象将会导致bug);

mybatis采用的是接口式编程

一个dao接口对应一个高级映射配置文件

虽然这个接口没有实现类,但是mybaits会为其生成一个代理对象.

@Test	public void getEmployees2() {		SqlSession session=null;		try {			session=getSqlSessionFactory().openSession();			//得到的是一个接口实现类代理对象			EmployeeMapper mapper=session.getMapper(EmployeeMapper.class);			System.out.println(mapper.getEmployeeById(1).toString());		} catch (IOException e) {			// TODO Auto-generated catch block			e.printStackTrace();		}finally{			session.close();		}	}

 核心配置文件的详解

properties

使用这个标签来引入外部的properties文件

resource属性:引入类路径下的资源

url:是引入网络路径或者磁盘路径下的资源

1  

setting

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

typealiases

这个标签视为一些复杂的名称对象取别名来简化书写       默认为类名首字母小写

如果一个一个取别名,那么就会显得很累!所以又有一个批量取别名的标签

 

typehandlers

类型处理器

将java中的sting类型转化为数据库中的varchar()类型     要想自定义类型处理器   需要实现typeHandler接口或者继承basetypeHandler

和上面的标签一样,引入你自定义的类型处理器   还有批量引入自定义的类型处理器    默认的类型处理器很多

自定义类型处理器

// ExampleTypeHandler.java@MappedJdbcTypes(JdbcType.VARCHAR)public class ExampleTypeHandler extends BaseTypeHandler
{ @Override public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, parameter); } @Override public String getNullableResult(ResultSet rs, String columnName) throws SQLException { return rs.getString(columnName); } @Override public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return rs.getString(columnIndex); } @Override public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return cs.getString(columnIndex); }}

然后引入

plugins

MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

通过实现拦截器接口通过@inceptor注解来配置拦截的sql语句方法

// ExamplePlugin.java@Intercepts({@Signature(  type= Executor.class,  method = "update",  args = {MappedStatement.class,Object.class})})public class ExamplePlugin implements Interceptor {  public Object intercept(Invocation invocation) throws Throwable {    return invocation.proceed();  }  public Object plugin(Object target) {    return Plugin.wrap(target, this);  }  public void setProperties(Properties properties) {  }}

 

environments

配置运行环境,比如事务管理器等

 

databaseisProvider

数据库厂商配置

得到数据库厂商的标识

 

mappers

导入映射文件

class属性   直接引用接口

接口与其对应的sql映射文件在一块并且名字要相同,class引用的全类名接口才有效

1.有接口对应的映射文件

2.没有,在接口上用注解来编写sql语句

 

这些标签是有顺序的不能随便打乱顺序

映射文件的详解

几个主要标签

cache

命名空间的二级缓存配置

cache-ref

其他命名空间缓存配置的引用   表示和那个命名空间的缓存一致

resultmap

自定义结果集映射

sql

抽取可重用语句块

-->    
lastname,password,email,did
lastname,password,email,did

 

insert

update

delete

select

 

数据库自增主键问题

mysql

mysql支持自增主键,mybatis自增主键值的获取,也是利用statement.getGeneratekeys()

insert标签的useGeneratekeys属性是使用主键获取主键值策略

keyProperty:指定对应的主键属性,也就是mybatis获取主键以后,将这个值封装javaBean哪个属性

 

要想让接口有返回值,直接修改接口的方法的返回值就可以了

mybati不但支持查询返回结果,还支持增删改定义以下返回值  Integer long  Boolean

 

oracle

oracle不支持自增        oracle使用序列来模拟自增

如何获取这个序列值

before 在sql运行之前运行

after   在sql运行之后运行

select tb_employee_seq.currval from dual
insert into tb_employee(id,lastname,password,email) values(tb_employee_seq.nextval,#{lastName},#{password},#{email})

 

参数处理

dao的方法中的参数

单个参数,mybatis不会做特殊处理

#{}  不在乎大括号内是什么

多个参数   mybatis会做特殊处理    会把多个参数封装成一个map  

那怎么获取参数呢?

通过key来   注意此时的key也比较特殊是固定的param1 ....paramN

命名参数

明确指定封装参数是map的key:@param("id")

key:使用@Param注解指定的值

 

pojo

如果多个参数正好是我们业务逻辑的参数模型,我们就可以直接传入pojo

#{属性名}

 

map

如果多个参数不是我们业务逻辑的数据模型,没有对应的pojo,而且不经常用,那么我们就传入一个map

 

如果多个参数不是我们业务逻辑i的数据模型,但是要经常使用,那么我们可以根据参数,来建立一个数据传输对象,参数作为他的属性

他还支持级联属性

 

注意:如果参数是CollecctIon与数组的话,mybatis也会做特殊处理 ,也是把传入的集合或者数组封装在一个map中

key:如果参数为Collection类型的  那么key就是collection   如果是list那么就可以是list    如果是数组,那么是array

 

参数值的获取

#{                          }                             ${                              }

在sql中是以占位符?                          在sql中是值直接拼装的

以预编译的形式,将参数设置到sql语句中

preparedstatement:防止sql注入

 

原生的jdbc不支持#{}  我们可以使用${}进行取值

比如分表   按照年份分表拆分

select  * from  ${year}_salary  where ***;

select * from tb_employee order by ${f_name};--作为条件

#{              }更丰富的用法

规定参数的一些规则

javatype   jdbctype   mode   numericscale   resultmap   typehandler   jdbctypeName

 

oracle不支持null  因为默认的为other

如果参数值为null时,会报错,但是mysql没问题

oracle  对所有的null映射的是原生jdbc的other

由于全局配置中的JdbcTypeForNull的默认值为other

所以修改全局配置

 

select 返回值问题

如果返回的是一个集合,那么resulttype中为集合中元素的全类名

 

如果返回的是一条记录的map,key就是列名

resulttype="map";

如果返回的是多条记录的map   需要在dao中的方法上加@mapkey注解来指定那个属性来作为key  通过这个key来取值

map<Integer,employee>              resulttype为返回值的全类名

        主键     javaBean

//返回一条记录的map   sql映射文件resultType 为map    public Map
getEmpLike(String lastName,String lastName2); //返回多条记录的map sql映射文件的resultType为Employee 要想指定他的key为id那么使用@MapKey注解 @MapKey("eid") public Map
getEmpLikeMapMany(String lastName);

 

自定义结果集的映射

resultMap标签的使用

外部的resultmap的命名引用和resulttype属性不能同时使用

自定以返回结果的规则

定义主键mybatis会有优化

 

关联查询

resultmap标签的使用

 一对多  多对多的时候级联属性封装结果集

resultmap中有一个标签association(一对一的情况下使用这个) 可以指定联合的javabean对象

Property 指定哪个属性是联合的对象

javaType指定这个对象的类型

但是这样sql语句太过复杂,所以可以使用association进行分步查询

 

先查出员工,然后根据远的的外键,再根据外键到部门查询到所对应的对象   分布查询association中的3个重要属性  需要用到两个方法

Property    指定那个属性是联合的对象

select     查询id的方法

column   指定将哪一列的值传给select标签中的方法     查出部门对象赋给员工的部门属性

分步查询可以使用到延迟加载,查询效率高

在全局配置中配置

association 是关联对象类型的属性的封装

一对多的时候使用collection标签

collection定义关联集合类型的属性的封装规则

同样的有复杂的sql语句的

property  集合属性名

oftype  集合中元素的全类名

 

有分布查询的(可以使用懒加载避免浪费)与association类似

Property  集合属性名

select   查询员工的方法    

column    多列的值传递过去,将多列的值封装map传递   “{key1=column1,key2=column2}”    key为你要传入第二个方法中的值的key而且要与第二个方法中的参数#{}大括号中的值一样    column为你你要传入第二个方法的值,它为对应的当前这个对象表格中的主键的字段名

fetchtype="lazy"  使用懒加载   如果eager的话立即

 

   discriminator标签(if else)

他是一个鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为

    

 

动态sql

4个对sql语句进行操作的重要标签

if

对参数中取值进行判断

id=#{id}

查询的时候如果条件没带可能sql拼装会有问题   解决方案:

1.1=1

2.使用where标签   where只会去掉第一个多出来的and

 

choose

trim

foreach

属性的介绍

collection:指定要遍历的集合

list类型的参数会做特殊处理封装在map中,map的key就叫list   也可以使用@param注解来指定集合的引用

item:将当时遍历出的元素赋值给指定的变量

separator:每个元素之间的分隔符

open:以。。。开始的字符

close:以。。。结束的字符

index:索引   遍历list和map的时候的索引是key  

批量插入

insert into tb_employee(
) values(#{emp.lastName},#{emp.password},#{emp.email},#{emp.document.id})
insert into tb_employee(
) values
(#{emp.lastName},#{emp.password},#{emp.email},#{emp.document.id})

oracle只有一种,mysql有两种,mysql要想一条语句允许使用“;”的话,来分隔多条语句,要在url配置?allowmultiqueries=true

 

利用中间表进行批量插入

就是oracle中特殊的插入方法

sql语句如下

insert into tb_employee (id,lastname,email) seleect tb_employee_seq ,lastname,email from (  --虚表作为一个中间表 select ' test_lastnam01' as lastname,'test_email01' as email form dual union ......);

内置参数

_parameter  代表整个参数

如果是传入单个参数,那个这个_parameter就是这个参数

如果传入多个参数,那么这个_parameter就是这个多个参数封装成的map

_databaseId  代码数据库的别名

 

bind标签的使用

bind可以将ognl表达式的值绑定到一个变量当中,方便后来引用这个变量的值

注意:

value中的lastName一定要是javaBean中的属性名,不能是随便的一个

 

sql标签

抽取可重用的sql片段 方便后面引用

比如1.比如你要查询的字段列 还有你要插入的字段列
2.include来引用已经抽取的
3.include中还可以自定义一些属性property sql标签内部就可以使用的属性
使用${} 不能使用#{}

lastname,password,email,did
lastname,password,email,did

进行引用

insert into tb_employee(
) values(#{emp.lastName},#{emp.password},#{emp.email},#{emp.document.id})

 

缓存机制

一级缓存

默认打开的

作用范围是sqlsession的范围

与数据库同一次会话期间,查询到的数据会放在本地缓存当中,以后如果需要获取相同的数据,直接从缓存中拿,不用再去数据库查询

 

一级缓存失效的情况

1.不同的sqlsession

2.相同的sqlsession,不同的查询条件

3.sqlsession相同,但是两次查询之间进行了增删改(这次增删改可能改变了你要查询的)

4.sqlsesion相同,但是一级缓存被清空了

二级缓存

基于namespace级别的缓存,一个namespace对应一个二级缓存

 

工作机制:

1.1个会话,查询一条数据,查询到的数据会被放在一级缓存当中

2.如果会话关闭,一级缓存当中的数据会被放入到二级缓存当中,新的会话,就可以参照二级缓存中的数据

3.不同的namaspace查出的数据会放在自己的缓存当中

 

二级缓存的使用及细节

1.开启全程配置cacheEnabled

 

 

2.在对应的namespace文件中配置二级缓存 

 

cache标签

   cache标签的详解

  属性:

 eviction:缓存的回收策略,默认值为LRU

值:

 LRU 最近最少使用;移除最长时间不被使用的对象

FIFO 先进先出 ;按对象进入缓存中的顺序来移除他们

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

weak:弱引用;更积极地移除基于垃圾收集器状态和弱引用规则的对象

FlusHInterval:缓存刷新间隔

缓存多长事件清空一次,默认不清空,设置一个毫秒值

readonly:是否只读,默认为false

true:只读:mybatis会认为 所有从缓存中获取数据的操作都是只读操作,不会修改数据

mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户,不安全但是速度快

false:不只读 mybatis觉得数据会被修改,会利用序列化和反序列的技术克隆一份新的数据给你,安全,但是速度慢。

size:缓存当中存放多少数据

type:指定自定义缓存的全类名    

   要想自定义缓存,实现cache接口

3.因为readonly的默认值为false ;mybatis会利用反序列化和序列化技术克隆一份新的数据给你。所以你使用的pojo需要实现序列化接口(javaBean种的对象实现序列化接口)

public class Employee implements Serializable{

 

细节:不同的会话,但要相同的SqlSessionFactory

      会话关闭,才会放入到二级缓存当中

    查出的数据会放入一级缓存当中,只有这个一级缓存当中的会话关闭,一级缓存当中的数据才会放入二级缓存当中

package com.jdztc.mybatis.test;import java.io.IOException;import java.io.InputStream;import java.util.ArrayList;import java.util.List;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.Test;import com.jdztc.mybatis.dao.EmployeeMapper;import com.jdztc.mybatis.javabean.Document;import com.jdztc.mybatis.javabean.Employee;public class MyBatisTest {    public static SqlSessionFactory getSqlSessionFactory() throws IOException{        String resource = "mybatis-config.xml";        InputStream inputStream = Resources.getResourceAsStream(resource);        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);        return sqlSessionFactory;    }    @Test    public void getEmpolyees() throws IOException {                SqlSession session = getSqlSessionFactory().openSession();        try {            Employee employee = session.selectOne("com.jdztc.mybatis.dao.EmployeeMapper.getEmployeeById", 1);            System.out.println(employee.toString());        }finally {            session.close();        }    }    //一级缓存体验    @Test    public void test01() throws IOException {        SqlSession session=getSqlSessionFactory().openSession();        try {                        //得到的是一个接口实现类代理对象            EmployeeMapper mapper=session.getMapper(EmployeeMapper.class);            Employee emp01=mapper.getEmployeeById(1);            Employee emp02=mapper.getEmployeeById(1);            System.out.println(emp01);            System.out.println(emp02);                    }finally{            session.close();        }    }    //不同的会话 SqlSession导致一级缓存失效    @Test    public void getEmployees2() throws IOException {        SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();        SqlSession session=sqlSessionFactory.openSession();        SqlSession session1=sqlSessionFactory.openSession();        try {                        //得到的是一个接口实现类代理对象            EmployeeMapper mapper=session.getMapper(EmployeeMapper.class);            EmployeeMapper mapper1=session1.getMapper(EmployeeMapper.class);            Employee emp01=mapper.getEmployeeById(1);            Employee emp02=mapper1.getEmployeeById(1);            System.out.println(emp01);            System.out.println(emp02);                    }finally{            session.close();        }    }    //相同的会话查询条件不一样 会导致一级缓存不一样    @Test    public void test02() throws IOException {        SqlSession session=getSqlSessionFactory().openSession();        try {                        //得到的是一个接口实现类代理对象            EmployeeMapper mapper=session.getMapper(EmployeeMapper.class);            Employee emp01=mapper.getEmployeeById(1);            Employee emp02=mapper.getEmployeeById(2);            Employee emp03=mapper.getEmployeeById(2);            System.out.println(emp01);            System.out.println(emp02);            System.out.println(emp03);                    }finally{            session.close();        }    }    //相同的会话两个查询之间进行了增删改    //还有一个调用清除缓存的办法   sesion.clearCache()    @Test    public void test() throws IOException{        SqlSession session=getSqlSessionFactory().openSession();        try {            EmployeeMapper mapper=session.getMapper(EmployeeMapper.class);            Employee emp01=mapper.getEmployeeById(1);            List
emps=new ArrayList
(); Document document=new Document(); document.setId(1); emps.add(new Employee(null, "名邦敏", "123", "345@qq.com",document)); mapper.addEmp(emps); Employee emp02=mapper.getEmployeeById(1); System.out.println(emp01); System.out.println(emp02); } finally { // TODO: handle finally clause session.close(); } } //二级缓存体验 @Test public void testCache2() throws IOException{ //注意:要使用相同的sqlSessionFactory SqlSessionFactory sqlSessionFactory=getSqlSessionFactory(); SqlSession session=sqlSessionFactory.openSession(); SqlSession session2=sqlSessionFactory.openSession(); try { EmployeeMapper mapper=session.getMapper(EmployeeMapper.class); EmployeeMapper mapper2=session2.getMapper(EmployeeMapper.class); Employee emp01=mapper.getEmployeeById(1); System.out.println(emp01); session.close(); Employee emp02=mapper2.getEmployeeById(1); System.out.println(emp02); session2.close(); System.out.println(emp01==emp02); } finally { // TODO: handle finally clause } }};

 

 

二级缓存中的配置

1.如果在全局配置中cacheEnabled的值为false  那么只会关闭二级缓存,不会关闭一级缓存

2.每个select标签都有usercache="true" 默认为true   如果为false,那么就不使用二级缓存,使用一级缓存

3.每个增删改都有flushcache这个属性   如果他的属性值为true,那么每次增删改都会清空二级缓存,也会清空一级缓存           select 是不会清空的他的默认值为false

4.sqlsession的clearcache只是清除当前session的一级缓存

5.全局配置当中的localcachescope本地缓存作用域(一级缓存)

  当前会话的所有数据保存在会话缓存当中,statement:可以禁用掉一级缓存

 

使用第三方缓存(以ehcache为例)

1.导入jar包

ehcache 的核心包    slf4j-api-1.6.1 slf4j-log4j-1.6.2

2.导入mybatis整合他的核心包

3.创建ehcache.xml文件

如果其他命名空间想要和他一样的缓存配置可以使用 cache-ref  标签

 

 

mybatis-spring整合

1配置事务管理器

2.开启注解事务

3.创建sessionfactoryBean 不用写那个创建sqlSessionfactory实例代码

 

4.扫描所有的mapper接口,让这些mapper能够自动注入

mybatis-spring:scan  标签

也可以使用配置Bean的方式

MapperScannerConfigurer  中的属性basepackage属性值为接口包名

 

转载于:https://www.cnblogs.com/fupengpeng/p/7531925.html

你可能感兴趣的文章
Jenkins安装配置
查看>>
个人工作总结05(第二阶段)
查看>>
Java clone() 浅拷贝 深拷贝
查看>>
深入理解Java虚拟机&运行时数据区
查看>>
02-环境搭建
查看>>
spring第二冲刺阶段第七天
查看>>
搜索框键盘抬起事件2
查看>>
阿里百川SDK初始化失败 错误码是203
查看>>
透析Java本质-谁创建了对象,this是什么
查看>>
BFS和DFS的java实现
查看>>
关于jquery中prev()和next()的用法
查看>>
一、 kettle开发、上线常见问题以及防错规范步骤
查看>>
eclipse没有server选项
查看>>
CRC码计算及校验原理的最通俗诠释
查看>>
QTcpSocket的连续发送数据和连续接收数据
查看>>
使用Gitbook来编写你的Api文档
查看>>
jquery扩展 $.fn
查看>>
Markdown指南
查看>>
influxDB的安装和简单使用
查看>>
JPA框架学习
查看>>