杨泽龙❤

这是一个代码的世界

MyBatis

MyBatis

一,开发所依赖的jar文件

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>

 

####二.接口式编程

Mapper配置文件中,的名称空间指定接口的全类名;

package com.yangzlong.mybatis.DAO;

import com.yangzlong.mybatis.bean.Employee;
//接口
public interface EmployeeMapper {

public Employee getEmpById(Integer 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.yangzlong.mybatis.DAO.EmployeeMapper">
 <select id="getEmpById" resultType="com.yangzlong.mybatis.bean.Employee">
  select * from tbl_employee where id = #{id}
 </select>
</mapper>

这样就实现了接口和mapper文件的动态绑定;

测试:

    @Test
public void test01() throws IOException {
     //获取sqlSessionFactory对象
SqlSessionFactory sqlSession = getSqlSessionFactory();
     //获取SQLSession对象
SqlSession session = sqlSession.openSession();
try {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
System.out.println(mapper);
Employee empById = mapper.getEmpById(1);
System.out.println(empById);
} finally {
session.close();

}

}

测试结果:

org.apache.ibatis.binding.MapperProxy@5034c75a
Employee [id=1, lastName=null, gender=0, email=tom@163.com]
结论:

==通过sqlSession对象和接口绑定通过调用getMapper方法获取接口的实现类,是通过Mybatis实现的,mybatis通过反射得到一个代理对象org.apache.ibatis.binding.MapperProxy@5034c75a用代理对象进行增删改查的操作;实现了接口式编程将DAO层分开,为后期开发和扩展做了很好的铺垫,推介使用这种获取mapper实现类的方式。好处可以解耦,类型检查等==

  1. sqlSession对象代表了和数据库的一次会话交互,用完必须关闭;

  2. sqlSession底层其实就是维护了一个connection对象,所以他两一样都是非线程安全的;每次使用都需要获取新的对象,所以不能把它放在共享的成员变量中;(private SqlSession sqlSession这样在多线程环境中会出错)

  3. mapper接口没有实现类,但mybatis会为这个接口生成一个代理对象(通过xml和接口的绑定)

    EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);

    Mybatis规定通过Sqlsession的getMapper方法得到接口类型的代理对象进行相应的增删改查操作;

三,MyBatis全局配置文件properties引入外部文件

配置数据源通过外部文件引入,使用properties标签,后期mybatis整合spring时数据源等信息陪 都交给spring来完成不做重点,了解记忆;

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=123456
<configuration>
<properties resource="jdbc.properties"></properties>
<environments default="development">
<environment id="development">
<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>
</environments>
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
</configuration>

####四,MyBatis全局配置文件settstings运行时行为设置(行为处理器)

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

具体可参考官方文档进行设置;

实例:

<!--开启驼峰命名规范-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

五,MyBatis全局配置文件typeAliases别名处理器

类型别名是为 Java 类型设置一个短的名字。 它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。

使用typeAliases标签可以给Java类型起一个短别名;

<typeAliases>
<!-- <typeAlias type="com.yangzlong.mybatis.bean.Employee" alias="emp"/> -->
<package name="com.yangzlong.mybatis.bean"/>
</typeAliases>

这里介绍三种:

1,直接使用typeAlias标签指定类型的全类名,默认别名为类名首字母小写;

2,使用package标签进行批量修改别名,只要是在该包下面的或者是子包全部扫描,默认类名首字母小写;

3,使用注解@Alias("要起得别名")

PS:如果使用package标签批量操作起别名,包下h或者子包出现相同的类型或者相同类名时使用注解区分;

但建议使用全类名;

####六,MyBatis全局配置文件typeHandlers类型处理器

无论Mybatis在预处理sql中设置参数,或者在结果集总取出一个值得时候都会用类型处理器来获取值转化整java类型

七,MyBatis全局配置文件plugins插件

MyBatis允许在已经运行的语句中一点进行拦截,plugins插件标签就是进行拦截的;

主要对已下对象进行拦截:

Executor执行器对象

parameterHandler参数执行器对象

ResultSetHandler返回结果集对象

StatementHandler sql执行器对象

只需实现Intercaptor接口;

八,MyBatis全局配置文件environments环境配合器

 

MyBatis可以配置多个环境,例如开发环境,测试环境等;可以通过environments标签来指定。

注意:虽然可以配置多个环境,但是每个环境只能有一个SqlSessionFactroy对象

environment标签必须要有DataSource和transactionManage;


<environments default="development">
<environment id="development">
<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>
</environments>

default:指当前环境为哪种环境;

id:当前的唯一标识,标识当前环境的唯一标识(名称);

transactionManager:事务管理器;可以自定义事务管理器,只需要实现transactionManagerFactory接口;

type:当前事务的类型;JDBC(支持回滚,提交等操作依赖于数据源中得到的事务)|MANAGED(几乎没有什么配置,不会提交或者回滚数据,取决于J2ee的容器决定);

datasource:数据源配置;根据property属性选择器来指定属性值;

type:数据源的类型;公有三种数据源类型:POOLED(使用连接池技术)

|UNPOOLED(不使用连接池技术)

|JNDI(使用jndi用于EJB服务)

同样的也可以自定义数据源,实现DataSourceFactory接口,type就是全类名;

九,MyBatis全局配置文件,DataSourceIdProvider支持多数据库

可以指定多个数据库厂商,进行数据操作;通过DataSourceIdProvider来指定数据库的连接,

<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>

可以指定多个数据库标识;type=”DB_VENDOR“底层是根据驱动获取数据库厂商标识;

可以通过environment的id属性指定对应的数据库连接;

指定Mapper映射文件中sql映射databaseId指定相应的数据库支持;

十,MyBatis全局配置文件,Mapper映射器

mapper映射器是将sql映射文件注册到MyBatis的全局配置当中,然后MyBatis执行sql映射文件中的sql语句;

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

mapper:表示注册一个sql映射文件;

配置文件注册:

resource:类路径下指定sql映射文件的位置(通常单独定义个包,里面放sql映射文件)

url:引用网络上或者是磁盘上的映射文件;

注册接口:

class:使用class引用接口注册,(注意:接口和sql映射文件必须在同一个目录下,而且名字要相同)

基于注解:

MyBatis支持注解的方式,通过在接口是指明@select等增删改查方法;

还可以使用package标签批量注册;name属性为包名;

十一,MyBatis sql映射文件中insert_update获取自增主键的值

想要获取自增主键的值,如果数据库中主键是自增的,要获取主键值,可以使用useGeneratedKeys默认值是false

注意:仅仅可以使用在insert和update有用;

 <insert id="addEmp" parameterType="com.yangzlong.mybatis.bean.Employee"
  useGeneratedKeys="true" keyProperty="id">
insert into tbl_employee (last_name,gender,email)values(#{lastName},#{gender},#{email})
 </insert>

useGeneratadKeys默认值是false,开启获取自增主键的话应该设置为true;

keyProperty指定主键映射封装到实体的哪个属性;

MyBatis底层是调用JDBC的getGenreatedkeys()方法来获取自增主键值;

####十二,MyBatis 获取非自增主键

oracle是不支持自增主键的,oracle是通过序列来模拟自增;

获取非自增主键值:

<insert id="addEmp" parameterType="com.yangzlong.mybatis.bean.Employee"
  useGeneratedKeys="true" keyProperty="id">
  <selectKey keyProperty="id" order="BEFORE" resultType="Integer">
  select tbl_employee nextval from dual
  </selectKey>
insert into tbl_employee (id,last_name,gender,email)values(#{id},#{lastName},#{gender},#{email})
 </insert>

使用selectKey来查询主键;

keyPeoperty:封装指定的对象属性;

order:在进行插入语句之前进行主键查询;

可以为after,值执行语句之后查询主键;

resultType:查询到结构的类型;

运行顺序:在进行插入语句之前查询非自增主键;

将查询到的id值封装给指定的属性;

再去运行插入语句,将封装好的id值取出来,进行插入语句的操作;

在进行插入语句之后获取主键:

<insert id="addEmp" parameterType="com.yangzlong.mybatis.bean.Employee"
  useGeneratedKeys="true" keyProperty="id" >
  <selectKey keyProperty="id" order="AFTER">
  select tbl_employee currval from dual
  </selectKey>
insert into tbl_employee (id,last_name,gender,email)
values(employe_seq.nextval,#{lastName},#{gender},#{email})
 </insert>

运行流程:

先进行插入,在序列中取到主键;

在进行selectKey主键的查询,将主键取出来;

总结:在实际开发中,用的最多的是before在插入之前取主键,after的方式是记录最后一次执行sql时的主键,所以建议使用before的方式;

十三,MyBatis映射文件参数处理,单个参数和多个参数的处理

单个参数:

public Employee getEmpById(Integer id);

单个参数MyBatis不会进行特殊的封装,#{参数名},直接可以取出数据;

 

多个参数:

public Employee getEmpIdAndLastName(@Param("id")Integer id,@Param("lastName")String lastName);

当多个参数的时候,MyBatis会进行特殊的封装,多个参数会被封装成一个map,#{}就是从map中获取指定key值,所对应的value;

key:Parma1····ParmaN;

value:就是传入的参数的值;

解决方法:

因为参数会被MyBatis进行特殊封装成一个map,会报绑定异常,底层封装是通过map(key,value)

key:param1...paramN,value就是传入参数的值,可以通过指定key来获取value的值,也可以根据下标索引的方式来指定key所对应的值;

方式一:

<select id="getEmpIdAndLastName" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee where id=#{param1} and last_name=#{param2}
</select>

方式二:

<select id="getEmpIdAndLastName" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee where id=#{0} and last_name=#{1}
 </select>

方式三:

命名参数:@param(" ")

public Employee getEmpIdAndLastName(@Param("id")Integer id,@Param("lastName")String lastName);

通过在接口方法中添加@param("指定的参数名"),来的指定参数的一一对应;也是最推介的一种方式;

十四,MyBatis映射文件 参数处理

如果在传入多个参数的时候,正好是业务中的数据模型(实体bean),我们就可以直接传入POJO;

同时也可以自定义,可以传入map集合

map(key,value)

源码分析:

 public Object getNamedParams(Object[] args) {
   final int paramCount = names.size();
   if (args == null || paramCount == 0) {
     return null;
  } else if (!hasParamAnnotation && paramCount == 1) {
     return args[names.firstKey()];
  } else {
     final Map<String, Object> param = new ParamMap<Object>();
     int i = 0;
     for (Map.Entry<Integer, String> entry : names.entrySet()) {
       param.put(entry.getValue(), args[entry.getKey()]);
       // add generic param names (param1, param2, ...)
       final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
       // ensure not to overwrite parameter named with @Param
       if (!names.containsValue(genericParamName)) {
         param.put(genericParamName, args[entry.getKey()]);
      }
       i++;
    }
     return param;
  }
}

总结:参数封装,参数多的时候MyBatis会将参数封装成一个Map,为不混乱可以使用@Param注解来指定封装时的key,#{key}就能取出map中的值;

十五,MyBatis映射文件 ${} #{}参数值得获取

区别:#{}是以预编译的形式将参数设置到sql语句中,防止了sql注入;

${}是将取出的值,直接拼接到sql语句中,会有安全问题;

在原生sql中如不需要动态获取参数的情况下:例如,进行年份分库分表之后需要传入固定的表名的时候就应该使用${}进行取值拼接操作;

十六,MyBatis映射文件 select查询语句返回List集合

public List<Employee> getListByLastNameLike(String lastName);
<select id="getListByLastNameLike" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee where last_Name like #{lastName}
 </select>

resultType:返回结果类型为返回元素的类型;并不是返回为List类型;

测试:

@Test
public void test07() throws IOException {
SqlSessionFactory sessionFactory = getSqlSessionFactory();
SqlSession session = sessionFactory.openSession();
try {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
List<Employee> list = mapper.getListByLastNameLike("%e%");
for (Employee employee : list) {
System.out.println(employee);
}
} finally {
session.close();
}
}

十七,MyBatis映射文件 select查询封装Map

对单条记录的封装,将查询出的数据封装成一个map

public Map<String,Object> getMapById(Integer id);
<select id="getMapById" resultType="map">
select * from tbl_employee where id=#{id}
 </select>

封装单条记录为map集合,resultType:返回结果类型为map类型(MyBatis已经自动封装了好类型)

测试:

    @Test
public void test08() throws IOException {
SqlSessionFactory factory = getSqlSessionFactory();
SqlSession session = factory.openSession();
try {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Map<String, Object> map = mapper.getMapById(2);
System.out.println(map);
} finally {
session.close();
}

}
{gender=1, id=2, last_Name=jreey, email=jreey@163.com}

 

多条记录进行封装:

@MapKey("id")
public Map<Integer,Employee> getMapByIdLike(String lastName);

@MapKey(“id”)根据id为列名进行查询. Map集合中key表示为列名(记录主键),value就表示为值(返回javaBean类型);

 <select id="getMapByIdLike" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee where last_Name like #{lastName}
 </select>

多条记录进行封装成Map,映射文件中resultType:返回值类型为javabean类型;(我们要做的是封装一个map集合,集合的value是一个JavaBean对象所以返回结果类型应该是javaBean的类型)

测试:

@Test
public void test09() throws IOException {
SqlSessionFactory factory = getSqlSessionFactory();
SqlSession session = factory.openSession();
 try {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Map<Integer, Employee> map = mapper.getMapByIdLike("%r%");
System.out.println(map);
} finally {
session.close();
}
}

{2=Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com],
3=Employee [id=3, lastName=jreey, gender=1, email=jreey@163.com],
4=Employee [id=4, lastName=jreey, gender=0, email=jreey@163.com]}

十八,MyBatis映射文件 select查询 resultMap自定义结果集映射规则

resultType:MyBatis会进行自动封装的相应的指定的javaBean类型;根据我们对其的指定自动进行封装;

resultMap:自定义封装,在没对应的JavaBean时自定义封装类型,换而言之:如果查出的数据和javaBean的属性没有进行一一对应时就可以使用resultMap自定义映射规则;

<resultMap type="com.yangzlong.mybatis.bean.Employee" id="myemp">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
 </resultMap>
 
 <select id="getEmp" resultMap="myemp">
select * from tbl_employee where id=#{id}
 </select>

type:自定义结果集的类型;

id:自定义结果集映射规则的唯一ID;

column:数据库表中的字段;

property:java实体中对应的属性;

测试:

@Test
public void test10() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmp(2);
System.out.println(emp);
} finally {
openSession.close();
}
}

 

十九,MyBatis映射文件,select查询,关联查询级联属性封装结果

如果业务需求中存在对象关联,可以使用ResultMap自定义封装结果集,比如数据库中存在多表的关联,在resultMap中就可以使用级联属性的方式进行自定义结果集的封装;

private Integer id;
private String lastName;
private String gender;
private String email;
private Department department;

在javaBean中存在对象关联

SELECT e.id id,e.last_name lastName,e.email email,e.gender gender,e.d_id did,d.id id,d.dept_name deptName
FROM tbl_employee e,tbl_dept d
WHERE e.d_id=d.id
AND e.id=2

数据库中进行多表关联查询

在Mapper映射文件中使用resultMap自定义封装映射结果集来对属性进行封装

<resultMap type="com.yangzlong.mybatis.bean.Employee"
id="MyEmpAndDept">
<id column="id" property="id" />
<result column="last_name" property="lastName" />
<result column="email" property="email" />
<result column="gender" property="gender" />
<result column="d_id" property="department.id" />
<result column="deptName" property="department.deptName" />
</resultMap>
<select id="getDeptAndEmpById" resultMap="MyEmpAndDept">
select e.id id,e.last_name lastName,e.email email,e.gender gender,e.d_id
did,d.id id,d.dept_name deptName
from tbl_employee e,tbl_dept d
where e.d_id=d.id
and e.id=#{id}
</select>

 

测试方法:

@Test
public void test11() throws IOException {
SqlSessionFactory sessionFactory = getSqlSessionFactory();
SqlSession session = sessionFactory.openSession();
try {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Employee id = mapper.getDeptAndEmpById(2);
System.out.println(id);
//JSONObject emp = new JSONObject(id);
//System.out.println(emp);
//int i = emp.length();
//System.out.println(i);
} finally {
session.close();
}
}

二十,MyBatis映射文件,select查询,resultMap自定义映射结果集,关联查询association定义关联对象封装规则

使用级联属性的方式可以自定义封装规则还可以使用association标签来指定联合查询;

可以使用association来关联单个对象

    <resultMap type="com.yangzlong.mybatis.bean.Employee" id="MyEmpAndDept2">
<id column="id" property="id" />
<result column="lastName" property="lastName" />
<result column="email" property="email" />
<result column="gender" property="gender" />

<association property="department" javaType="com.yangzlong.mybatis.bean.Department">
<id column="did" property="id"/>
<result column="deptName" property="deptName"/>
</association>
</resultMap>
<select id="getDeptAndEmpById" resultMap="MyEmpAndDept2">
select e.id id,e.last_name lastName,e.email email,e.gender gender,e.d_id
did,d.id id,d.dept_name deptName
from tbl_employee e,tbl_dept d
where e.d_id=d.id
and e.id=#{id}
</select>

使用association来联合查询;property:指定关联的对象属性名,JavaType:指定该对象的全类名;

 

二十一,MyBatis映射文件,select查询,resultMap自定义封装 结果集,使用association进行分步查询

上述介绍了自定义映射结果集中association的关联查询,association还可以进行分步查询,好处是:不用写复杂的多表关联sql语句,可以组合已有的方法,使用多个简单sql语句就可以实现需求,可读性高思路清晰;

DepartmentMapper.xml:

<mapper namespace="com.yangzlong.mybatis.DAO.DepartmentMapper">
<select id="getIdByDept" resultType="com.yangzlong.mybatis.bean.Department">
select id,dept_name deptName from tbl_dept where id=#{id}
</select>
</mapper>

EmployeeMapper.xml:

<resultMap type="com.yangzlong.mybatis.bean.Employee" id="myEmpStep">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<association property="dept"
select="com.yangzlong.mybatis.DAO.DepartmentMapper.getIdByDept" column="d_id">
</association>
</resultMap>

<select id="getEmpByIdStep" resultMap="myEmpStep" >
select * from tbl_employee where id=#{id}
</select>

使用association进行分步查询,property:employee中关联对象属性名称,

select:指定通过那个查询方法;

column:java实体中对象数据库字段;

思路:通过select指定的方法(插入column指定的参数值)查出对象,并封装给property指定的java属性;

测试及结果:

@Test
public void test12() throws IOException {
SqlSessionFactory sessionFactory = getSqlSessionFactory();
SqlSession session = sessionFactory.openSession();
try {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Employee step = mapper.getEmpByIdStep(2);
System.out.println(step);
} finally {
session.close();
}
}
Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com, dept=Department [id=1, deptName=开发部]]

二十二,MyBatis映射文件,select查询 基于resultMap中 association分步查询使用的延时加载(懒加载,或按需加载)

在mybatis进行分步查询时,如果只需要得到某一个单独的字段或者说一个对象时(因需求而定)使用了association的分步查询,这样的话就会进行两次查询,会访问两次数据库,既耗费了资源又浪费了时间,这时候我们就可以使用mybatist提供的延时加载,又叫懒加载;

<resultMap type="com.yangzlong.mybatis.bean.Employee" id="myEmpStep">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<association property="dept"
select="com.yangzlong.mybatis.DAO.DepartmentMapper.getIdByDept" column="d_id">
</association>
</resultMap>

<select id="getEmpByIdStep" resultMap="myEmpStep" >
select * from tbl_employee where id=#{id}
</select>

之前的配置文件不需要修该,在我们mybatis的全局配置文件中添加两个设置项就可以实现 懒加载:

<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>

lazyLoadingEnable:懒加载延迟加载开关,默认值是false,开启时所有的关联对象就会延迟加载;

aggressiveLazyLoading:默认值为false,禁止的时候我们需要的属性会按需加载,开启的话属性会全部加载;

 

####二十三,MyBatis映射文件,select查询_resultMap关联查询,collection定义关联集合封装规则

需求:查询部门的时候,将部门对应的所有员工信息查询出来; 之前的association是定义对象,现在我们要定义集合使用collection;

查部门的时候查询部门所有的员工;

    private Integer id;
private String deptName;
private List<Employee> emp;

每个不懵对应多个员工;一对多的关系;

接口方法:

public Department getDeptAndEmp(Integer id);

映射文件xml:

<resultMap type="com.yangzlong.mybatis.bean.Department" id="myDeptMap">
<id column="did" property="id"/>
<result column="dept_name" property="deptName"/>
<collection property="emp" ofType="com.yangzlong.mybatis.bean.Employee">
<id column="e_id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>

</collection>
</resultMap>

<select id="getDeptAndEmp" resultMap="myDeptMap">
SELECT
d.id did,d.dept_name dept_name,e.id e_id,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>

使用resultMap自定义结果集封装,集合使用collection来封装集合类型,

property:集合属性值;

ofType:定义集合属性的类型;

测试结果:

@Test
public void test13() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession session = sqlSessionFactory.openSession();
try {
DepartmentMapper mapper = session.getMapper(DepartmentMapper.class);
Department dept = mapper.getDeptAndEmp(2);
System.out.println(dept);
System.out.println(dept.getEmp());
} finally {
session.close();
}
}
Department [id=2, deptName=销售部, emp=[Employee [id=3, lastName=jreey, gender=1, email=jreey@163.com], Employee [id=7, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=8, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=10, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=11, lastName=tom2, gender=0, email=jreey@163.com]]]
[Employee [id=3, lastName=jreey, gender=1, email=jreey@163.com], Employee [id=7, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=8, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=10, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=11, lastName=tom2, gender=0, email=jreey@163.com]]

二十四,MyBatis映射文件 select查询_resultMap关联查询collection集合封装分步查询,延迟加载

之前使用association关联查询对象可以是使用分步查询,在使用collection封装集合的时候同样也可使用分步查询和使用延迟加载;

需求:根据部门Id查询该部门下所有的员工信息,要求使用分步查询;

查询方法:根据部门ID查询部门

public Department getDeptById(Integer deptId);

员工方法:根据部门ID查询所有的员工

public List<Employee> getEmpsByDeptId(Integer deptId);

步骤:通过部门ID查询部门信息,员工表中d_id为两表的关联外键,在员工表中查询需传入关联到部门的外键也就是部门id,即员工表中的d_id字段;

DepartmentMapper:

<resultMap type="com.yangzlong.mybatis.bean.Department" id="myDepts">
<id column="id" property="id"/>
<result column="dept_name" property="deptName"/>
<collection property="emp" select="com.yangzlong.mybatis.DAO.EmployeeMapper.getEmpByIdStep" column="id">

</collection>
</resultMap>

<select id="getDeptById" resultMap="myDepts">
select * from tbl_dept where id=#{id}
</select>

同样适用selcet属性指定方法进行参数查询(column参数指定数据库字段进行参数查询)然后封装给property指定的属性;

EmployeeMapper:

<select id="getEmpByIdStep" resultType="com.yangzlong.mybatis.bean.Employee" >
select * from tbl_employee where d_id=#{deptId}
</select>

测试结果:

Department [id=1, deptName=开发部, emp=[Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com], Employee [id=4, lastName=jreey, gender=0, email=jreey@163.com], Employee [id=5, lastName=tom, gender=0, email=jreey@163.com], Employee [id=6, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=9, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=12, lastName=tom2, gender=0, email=jreey@163.com]]]
[Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com], Employee [id=4, lastName=jreey, gender=0, email=jreey@163.com], Employee [id=5, lastName=tom, gender=0, email=jreey@163.com], Employee [id=6, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=9, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=12, lastName=tom2, gender=0, email=jreey@163.com]]

二十五,MyBatis映射文件,select查询_resultMap关联查询传递多列参数值&fetchType

不管使用的是association还是collection在这两个标签中都有column属性,对应的是传递参数给select查询方法并封装给property对应的对象属性或者集合属性,同时,也可传递多个参数;

可以通过将多列参数封装成map的形式来传递,如:column="{key1=column1,key2=column2}"封装成map集合的形式;

同时可以使用fetchType=“lazy”的配置实施延迟加载;

该属性可以取两个值:lazy:开启延迟加载配置;

eager:开启立即加载;

<collection property="emp" select="com.yangzlong.mybatis.DAO.EmployeeMapper.getEmpByIdStep" column="id"
fetchType="lazy">

</collection>
<association property="department" javaType="com.yangzlong.mybatis.bean.Department" fetchType="lazy">
		<id column="did" property="id"/>
		<result column="deptName" property="deptName"/>
</association>

####26,MyBatis映射文件,select_resultMap_discriminator鉴别器

这是最后一个resultMap中的标签,鉴别器driscriminator,主要是用来进行做鉴别处理的;指定一个属性并对他进行操作,例:age属性,当年龄大于18岁可以进行贷款申请,小于18岁不可以进行贷款申请;

<resultMap type="com.yangzlong.mybatis.bean.Employee" id="MyEmpDis">
		<id column="id" property="id"/>
		<result column="last_name" property="lastName"/>
		<result column="gender" property="gender"/>
		<result column="email" property="email"/>
		<discriminator javaType="string" column="gender">
			<case value="1" resultType="com.yangzlong.mybatis.bean.Employee">
				<id column="id" property="id"/>
				<result column="last_name" property="lastName"/>
				<result column="last_name" property="gender"/>
				<result column="email" property="email"/>
			</case>
			<case value="0" resultType="com.yangzlong.mybatis.bean.Department">
				<association property="dept" 
				select="com.yangzlong.mybatis.DAO.DepartmentMapper.getIdByDept" column="d_id">	
				</association>
			</case>
		</discriminator>
	</resultMap>

使用discriminatory进行鉴别处理:javaType:为指定该字段的数据类型;column:java实体所对应的字段类型;

case:所对应的该属性的鉴别规则,如上当性别为女时查询部门,性别为男时使用姓名做email;

用的不是很多,作为了解知识;

27,Mybatis动态SQL,if判断_OGNL表达式

动态SQL和JSTL语法很相似,都是基于xml中,但是Mybatis大大简化了这一操作,只需了解之前一办多的元素就可以,而且MyBatis使用了OGNL表达式来淘汰其他元素,大大简化了操作;

if元素:通过if元素可以对sql语句进行筛选和可选,判断其是否为空是否为null;

<select id="getEmp" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_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.trim()!=''">
and email=#{email}
</if>
<if test="gender==0 or gender==1">
gender=#{gender}
</if>
</select>

在日常拼接sql语句时可以使用if元素对sql进行判定;如果有该字段就进行查找,没有就不查找;

@Test
public void testDynimacSQL() throws IOException {
SqlSessionFactory factory = getSqlSessionFactory();
SqlSession session = factory.openSession();
try {
EmployeeMapperDynamicSQL mapper = session.getMapper(EmployeeMapperDynamicSQL.class);
Employee employee = new Employee(2, "%e%", null, "");
List<Employee> emp = mapper.getEmp(employee);
for (Employee e : emp) {
System.out.println(e);
}
} finally {
session.close();
}

}

结果:

2019-06-26 04:15:59,196 DEBUG [com.yangzlong.mybatis.DAO.EmployeeMapperDynamicSQL.getEmp] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:145) - ==>  Preparing: select * from tbl_employee where id=? and last_name like ? 
2019-06-26 04:15:59,274 DEBUG [com.yangzlong.mybatis.DAO.EmployeeMapperDynamicSQL.getEmp] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:145) - ==> Parameters: 2(Integer), %e%(String)
2019-06-26 04:15:59,314 DEBUG [com.yangzlong.mybatis.DAO.EmployeeMapperDynamicSQL.getEmp] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:145) - <==      Total: 1
Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com]

28,MyBatis动态SQL,where查询条件

在我们进行查询的时候有些条件没带,可能在SQL拼装的时候有问题;

如:我们要根据ID查数据,但是如果ID为null的时候SQL语句如下:

Preparing: select * from tbl_employee where and last_name like ? 

XML如下:

	<select id="getEmp" resultType="com.yangzlong.mybatis.bean.Employee">
  		select * from tbl_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.trim()!=''">
  			and email=#{email}
  		</if>
  		<if test="gender==0 or gender==1">
  			gender=#{gender}
  		</if>
  	</select>

如果ID为null的时候,失去了语句拼接就会有错误,select * from tbl_employee ==where and== last_name like ? where 关键字 和and,所有解决办法:

解决方案一:

<select id="getEmp" resultType="com.yangzlong.mybatis.bean.Employee">
  		select * from tbl_employee
  		where 1=1
  		<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.trim()!=''">
  			and email=#{email}
  		</if>
  		<if test="gender==0 or gender==1">
  			gender=#{gender}
  		</if>
  	</select>

通过给where后面添加一条1=1(条件为true)的方式,可以解决SQL拼接问题的发生;

解决方案二:

<select id="getEmp" resultType="com.yangzlong.mybatis.bean.Employee">
  		select * from tbl_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.trim()!=''">
	  			and email=#{email}
	  		</if>
	  		<if test="gender==0 or gender==1">
	  			gender=#{gender}
	  		</if>
  		</where>
  	</select>

使用where标签把所有条件查询条件全部包裹住,都写在where标签中;但必须拼接条件的and必须写在语句左边;

29,MyBatis动态SQL,trim字符串截取

我们知道,在使用where查询条件时,如果拼接SQL的语句关键字and或者or写在了条件的右边(后面),就会导致SQL语句错误,执行报错,所以我们使用trim来进行字符串的截取;

只要将所有条件执行SQL写在trim标签内,就相当于就这些条件语句进行拼串操作;

<select id="getEmpTrim" resultType="com.yangzlong.mybatis.bean.Employee">
  		select * from tbl_employee
  		<trim prefix="where" suffixOverrides="and">
  			<if test="id!=null">
  				id=#{id} and
  			</if>
  			<if test="lastName!=null and lastName!=''">
  				 last_name like #{lastName} and
  			</if>
  			<if test="email!=null and email!=''">
  				email=#{email} and
  			</if>
  			<if test="gender==0 or gender==1">
  				gender=#{gender}
  			</if>
  			
  		</trim>
  	
  	</select>

prefix:前缀,给整个trim标签中的串加一个前缀;

suffix :后缀 给整个trim标签包裹的串加一个后缀;

prefixOverrides:前缀覆盖,覆盖掉trim标签中串前面的某个字符;

suffixOverrides:后缀覆盖,覆盖掉trim标签中串后面的某个字符;

30,MyBatis动态SQL,choose分支选择

choose分支就类似switch-case分支结构,when就相当于每个case;

<select id="getEmpsChoose" resultType="com.yangzlong.mybatis.bean.Employee">
  		select * from tbl_employee
  		<where>
  		<choose>
  			<when test="id!=null">
  				id=#{id}
  			</when>
  			<when test="lastName!=null and lastName!=''">
  				last_name like #{lastName}
  			</when>
  			<when test="email!=null">
  				email=#{email}
  			</when>
  			<otherwise>
  				gender=0
  			</otherwise>
  			
  		</choose>
  		</where>
  	</select>

==在choose里面包裹的就相当于带了break的switch分支语句,每一个when就相当于case,当没有对应的条件时才进otherwise;==

31,MyBatis动态SQL,set与if结合动态跟新

在进行update更新操作的时候可以配合if标签一起使用,传入哪个字段就跟新哪个字段,如果没有传入的就不进行更新操作;使用set标签可以防止SQL拼接错误,在更新的时候会有多余逗号的干扰,可以使用set标签来解决,同样的也可以使用trim标签截取多余的逗号;

//更新操作,传入一个employee对象
public void updateEmp(Employee employee);

xml映射:

<update id="updateEmp">
  		update tbl_employee
  		set
  			<if test="lastName!=null">
  				last_name=#{lastName},
  			</if>
  			<if test="email!=null">
  				email=#{email}
  			</if>
  			<if test="gender!=null">
  				gender=#{gender}
  			</if>
  			where id=#{id}
  	</update>
出现SQL错误:
 Preparing: update tbl_employee set last_name=?, where id=? 

拼接的SQL语句出现了错误,多了一个逗号,导致程序报错;

解决方法一:

使用set标签:将所有更新条件全部包裹在内看成一个整体;

<update id="updateEmp">
  		update tbl_employee
  		<set>
  			<if test="lastName!=null">
  				last_name=#{lastName},
  			</if>
  			<if test="email!=null">
  				email=#{email}
  			</if>
  			<if test="gender!=null">
  				gender=#{gender}
  			</if>
  		</set>	
  			where id=#{id}
  	</update>
==>  Preparing: update tbl_employee SET last_name=? where id=?

这样就不会出现SQL拼接的问题;

解决方法二:

使用trim标签截取字符串:使用preffx设置前缀,使用suffixOverrides覆盖语句的逗号;

    <update id="updateEmp">
<!-- update tbl_employee
<set>
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email}
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</set>
where id=#{id} -->
update tbl_employee
<trim prefix="set" suffixOverrides=",">
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email}
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</trim>
where id=#{id}
</update>
==>  Preparing: update tbl_employee set last_name=? where id=? 

同样的可以可以避免sql拼接问题;

ps:在执行更新操作的时候记得要进行commit提交操作;

32,MyBatis动态SQL,foreach遍历集合

在开发中我们会遇到,一条SQL语句会查多条数据,比如where id in(?,?,?);类似于这样的操作,这时候就要用到foreach标签了;

通过多个ID查询多条数据;

public List<Employee> getEmpsFoeach(@Param("ids")List<Integer> ids);

xml映射文件:

<select id="getEmpsFoeach" resultType="com.yangzlong.mybatis.bean.Employee">
  		select * from tbl_employee where id in
  		<foreach collection="ids" item="item_ids" separator="," open="(" close=")">
  			#{item_ids}
  		</foreach>
  	</select>

通过foreach标签来遍历list集合:collection:遍历的是那种集合,上述为list,如果是map就写map;

item:将当前遍历的值赋值给指定的变量;(接口方法传进来的参数)

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

open遍历出所有元素拼接一个开始的字符;

close:遍历出所有元素拼接一个结束字符;

index:索引;

如果是list的时候就是指的索引;item就是每个元素的值;

如果是map的时候值得就是map中的key,item就是map中的value;

查询结果:

==>  Preparing: select * from tbl_employee where id in ( ? , ? ) 

33,MyBatis动态SQL,在mysql下foreach批量插入的两种方法

在我们添加数据的时候也可以使用foreach进行批量插入数据到数据库中;

第一种:

<insert id="addEmps">
insert into tbl_employee(last_name,email,gender,d_id)
values
<foreach collection="emps" separator="," item="emp" open="(" close=")">
#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id}
</foreach>
</insert>

第二种:

<insert id="addEmps">

<foreach collection="emps" separator=";" item="emp" >
insert into tbl_employee(last_name,email,gender,d_id)
values #{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id}
</foreach>
</insert>

直接使用封号隔开,发送多条SQl语句,但前提是必须打开mysql数据库连接属性,允许发送多条SQL;t同样的方法可以进行批量删除和批量修改,仅限于mysql;

jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true

34,MyBatis动态SQL,oracle下foreach批量操作的两种方法

刚刚使用mysql数据库,进行批量操作的方法,可以使用多条语句一起发送也可以使用类似VALUES(),(),()...的方法来进行批量操作,然而oracle不支持者中连续操作方式;

oracle不支持values(),(),(),()这种方式的操作;

我们可以是使用方式一:

使用begin end 来包裹插入语句;

    begin

insert into employees (employee_id,last_name,email)values

(employee_seq.nextval,'test01','test01@162.com');

insert into employees (employee_id,last_name,email)values

(employee_seq.nextval,'test01','test01@162.com');

end
<insert id="addEmps">

`<foreach collection="emps" item="emp" open="begin" close="end;">
insert into employees (employee_id,last_name,email)values

(employee_seq.nextval,#{emp.lastName},#{emp.email});
</foreach>

</insert>

方式二:可以使用中间表来进行批量插入操作;

insert into employees(employee_id,last_name,email) 
select employees_seq.nextval,lastName,email from(
     
      select 'test01' lastName,'test01@e11.com' email from dual
      union
      select 'test02' lastName,'test02@e11.com' email from dual
     
)

使用中间表的形式;

 <insert id="addEmps">
insert into employees(employee_id,last_name,email)
<foreach collection="emps" item="emp" open="select employees_seq.nextval,lastName,email from(" close=")" separator="union">
select #{emp.lastName} lastName,#{emp.email} email from dual
</foreach>
</insert>

35,MyBatis动态SQL,内置参数_parameter & _databaseId

MyBatis中内置了两个参数,

_paramter:当传进来是一个参数的时候parameter就代表这个参数,如果说传进来是多个参数时,mybatis会进行参数封装,将参数封装成一个map集合,此时这个集合就是parameter;

_databaseId:如果配置databaseprvoder,这个参数就产生了,databaseId就代表者配置数据库的别名;

例:


<select id="getEmpss" resultType="com.yangzlong.mybatis.bean.Employee">
<if test="_databaseId=='oracle'">
select * from employees
</if>
<if test="_databaseId=='mysql'">
select * from tbl_employee
</if>
</select>

根据不同数据库_databaseId来分别执行不同sql语句到不同数据库;

例:

 <select id="getEmpss" resultType="com.yangzlong.mybatis.bean.Employee">
<if test="_databaseId=='oracle'">
select * from employees
<if test="_parameter !=null">
where last_name=#{lastName}
</if>
</if>
<if test="_databaseId=='mysql'">
select * from tbl_employee
</if>
</select>

根据_parameter内置参数来判断传进来的参数,此时的parameter就相当于传进来的employee,也可以写写成

#{_parameter.lastName};

36,MyBatis动态SQL,绑定_bind

bind绑定参数操作;我们要进行模糊查询的时候,要求查询%e%名字中带有e的员工;

    emp.setLastName("e");

不在代码中直接写“%e%”可以在mapper中直接通过bind来绑定到参数中;

<select id="getEmpss" resultType="com.yangzlong.mybatis.bean.Employee">
<if test="_databaseId=='oracle'">
<bind name="_lastname" value="'%'+lastName+'%'"/>
select * from employees
<if test="_parameter !=null">
where last_name like #{_lastname}
</if>
</if>
<if test="_databaseId=='mysql'">
select * from tbl_employee
</if>
</select>

bind可以通过OGNL表达式的值来绑定到一个变量中,然后引用,<bind name="_lastname" value="'%'+lastName+'%'"/> 引用到#{_lastname};

37,MyBatis动态SQL,抽取可重用的SQL片段

可以将一些重复使用的字段全部提取出来

 <sql id="selectClom">
   <!--同样的也可以进行判断-->
  	 	<if test="_databaseId=='mysql'">
	  	 	last_name,gender,email,d_id
  	 	</if>
  	 	<if test="_databaseId=='oracle'">
  	 		employee_id,last_name,email
  	 	</if>
  	 </sql>

引用:

<select id="getEmpss" resultType="com.yangzlong.mybatis.bean.Employee">
  	 	<if test="_databaseId=='oracle'">
  	 	<bind name="_lastname" value="'%'+lastName+'%'"/>
  	 		select 
  	 			<include refid="selectClom"/>
  	 		 from employees 
  	 		<if test="_parameter !=null">
  	 			where last_name like #{_lastname}
  	 		</if>
  	 	</if>
  	 	<if test="_databaseId=='mysql'">
  	 		select 
  	 			<include refid="selectClom"/>
  	 		 from tbl_employee
  	 	</if>
  	 </select>

38,MyBatis缓存,介绍

MyBatis有两级缓存;

一级缓存:sqlsession,只要是同一个sqlsession对象就会自动启动一级缓存机制;sqlsession本身死一个map如果执行操作,会存在这个map中,再次执行的时候会先从map中查询,有就直接拿,没有就查询数据库;

缓存失效:1,不同的sqlsession;

2,sqlsession相同,查询条件不同;

3,sqlSession相同,在此之前执行了增删改操作可能对该数据有影响;

4,sqlSession相同,手动清除了一级缓存(clearCahe);

二级缓存:全局缓存,基于namespace级别的缓存,一个namespace对应一个二级缓存;

工作机制:1,一个会话查询一条数据,这个数据就会放到一级缓存中去,

2,如果会话关闭,数据会放到二级缓存中,新的会话查询就会参照一级缓存;

3,不同namespac查出来的数据会放在不同的自己对应的缓存中;(map)

####39,MyBatis缓存,二级缓存的使用

开启全局缓存;

MyBatis缓存<setting name="cacheEnable" value="true"/>

xml:

 <!--
  	eviction:缓存回收策略;
  	flushInterval:缓存刷新间隔的时间默认毫秒
  	readOnly:是否只读;
  		true:只读,速度快,不安全;
  		false:非只读;都有可能被修改;速度慢,安全
  	size:存放缓存的个数;
    -->
  	<cache eviction="FIFO" flushInterval="6000" readOnly="false" size="1024">
  	
  	</cache>

 

####40,MyBatis缓存,缓存的有关设置和属性

<setting name="cacheEnable" value="true"/>

*该设置是开启二级缓存;一级缓存是一直可用的;

*每个select标签都会有一个属性usercahe=“ true”;默认的是开启缓存,如果设置为false,一级缓存可以使用,二级缓存就会关闭;

*在使用一级缓存的时候,在同样的sqlSession的时候只要执行一次增删改操作,一级缓存就失效了,这是因为,在每个增删改标签上都有一个默认的属性:flashcahe=“true”,他会自动执行完后刷新缓存;清除掉缓存;同样的二级缓存也会被清除掉;在查询标签中flashcahe是false,如果改为true,每次查询都会清除,缓存就没有被使用到的;==增删改后一级二级都会清除==;

*opensession.clearCache()方法只会清除当前session的一级缓存;和二级缓存是没干系的;

*localcachescope:本地缓存作用域,SESSION|STATEMENT两个值,session就是一级缓存默认值;statement的时候禁用一级缓存;

41,MyBatis的工作流程

MyBatis框架主要分为以下几大层面;

==引导层==:基于xml配置;基于javaAPI;

==框架支持层==:主要有全局配置文件;以及配置方式:基于xml配置,基于注解配置;

MyBatis

一,开发所依赖的jar文件

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>

 

####二.接口式编程

Mapper配置文件中,的名称空间指定接口的全类名;

package com.yangzlong.mybatis.DAO;

import com.yangzlong.mybatis.bean.Employee;
//接口
public interface EmployeeMapper {

public Employee getEmpById(Integer 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.yangzlong.mybatis.DAO.EmployeeMapper">
 <select id="getEmpById" resultType="com.yangzlong.mybatis.bean.Employee">
  select * from tbl_employee where id = #{id}
 </select>
</mapper>

这样就实现了接口和mapper文件的动态绑定;

测试:

    @Test
public void test01() throws IOException {
     //获取sqlSessionFactory对象
SqlSessionFactory sqlSession = getSqlSessionFactory();
     //获取SQLSession对象
SqlSession session = sqlSession.openSession();
try {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
System.out.println(mapper);
Employee empById = mapper.getEmpById(1);
System.out.println(empById);
} finally {
session.close();

}

}

测试结果:

org.apache.ibatis.binding.MapperProxy@5034c75a
Employee [id=1, lastName=null, gender=0, email=tom@163.com]
结论:

==通过sqlSession对象和接口绑定通过调用getMapper方法获取接口的实现类,是通过Mybatis实现的,mybatis通过反射得到一个代理对象org.apache.ibatis.binding.MapperProxy@5034c75a用代理对象进行增删改查的操作;实现了接口式编程将DAO层分开,为后期开发和扩展做了很好的铺垫,推介使用这种获取mapper实现类的方式。好处可以解耦,类型检查等==

  1. sqlSession对象代表了和数据库的一次会话交互,用完必须关闭;

  2. sqlSession底层其实就是维护了一个connection对象,所以他两一样都是非线程安全的;每次使用都需要获取新的对象,所以不能把它放在共享的成员变量中;(private SqlSession sqlSession这样在多线程环境中会出错)

  3. mapper接口没有实现类,但mybatis会为这个接口生成一个代理对象(通过xml和接口的绑定)

    EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);

    Mybatis规定通过Sqlsession的getMapper方法得到接口类型的代理对象进行相应的增删改查操作;

三,MyBatis全局配置文件properties引入外部文件

配置数据源通过外部文件引入,使用properties标签,后期mybatis整合spring时数据源等信息陪 都交给spring来完成不做重点,了解记忆;

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=123456
<configuration>
<properties resource="jdbc.properties"></properties>
<environments default="development">
<environment id="development">
<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>
</environments>
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
</configuration>

####四,MyBatis全局配置文件settstings运行时行为设置(行为处理器)

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

具体可参考官方文档进行设置;

实例:

<!--开启驼峰命名规范-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

五,MyBatis全局配置文件typeAliases别名处理器

类型别名是为 Java 类型设置一个短的名字。 它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。

使用typeAliases标签可以给Java类型起一个短别名;

<typeAliases>
<!-- <typeAlias type="com.yangzlong.mybatis.bean.Employee" alias="emp"/> -->
<package name="com.yangzlong.mybatis.bean"/>
</typeAliases>

这里介绍三种:

1,直接使用typeAlias标签指定类型的全类名,默认别名为类名首字母小写;

2,使用package标签进行批量修改别名,只要是在该包下面的或者是子包全部扫描,默认类名首字母小写;

3,使用注解@Alias("要起得别名")

PS:如果使用package标签批量操作起别名,包下h或者子包出现相同的类型或者相同类名时使用注解区分;

但建议使用全类名;

####六,MyBatis全局配置文件typeHandlers类型处理器

无论Mybatis在预处理sql中设置参数,或者在结果集总取出一个值得时候都会用类型处理器来获取值转化整java类型

七,MyBatis全局配置文件plugins插件

MyBatis允许在已经运行的语句中一点进行拦截,plugins插件标签就是进行拦截的;

主要对已下对象进行拦截:

Executor执行器对象

parameterHandler参数执行器对象

ResultSetHandler返回结果集对象

StatementHandler sql执行器对象

只需实现Intercaptor接口;

八,MyBatis全局配置文件environments环境配合器

 

MyBatis可以配置多个环境,例如开发环境,测试环境等;可以通过environments标签来指定。

注意:虽然可以配置多个环境,但是每个环境只能有一个SqlSessionFactroy对象

environment标签必须要有DataSource和transactionManage;


<environments default="development">
<environment id="development">
<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>
</environments>

default:指当前环境为哪种环境;

id:当前的唯一标识,标识当前环境的唯一标识(名称);

transactionManager:事务管理器;可以自定义事务管理器,只需要实现transactionManagerFactory接口;

type:当前事务的类型;JDBC(支持回滚,提交等操作依赖于数据源中得到的事务)|MANAGED(几乎没有什么配置,不会提交或者回滚数据,取决于J2ee的容器决定);

datasource:数据源配置;根据property属性选择器来指定属性值;

type:数据源的类型;公有三种数据源类型:POOLED(使用连接池技术)

|UNPOOLED(不使用连接池技术)

|JNDI(使用jndi用于EJB服务)

同样的也可以自定义数据源,实现DataSourceFactory接口,type就是全类名;

九,MyBatis全局配置文件,DataSourceIdProvider支持多数据库

可以指定多个数据库厂商,进行数据操作;通过DataSourceIdProvider来指定数据库的连接,

<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>

可以指定多个数据库标识;type=”DB_VENDOR“底层是根据驱动获取数据库厂商标识;

可以通过environment的id属性指定对应的数据库连接;

指定Mapper映射文件中sql映射databaseId指定相应的数据库支持;

十,MyBatis全局配置文件,Mapper映射器

mapper映射器是将sql映射文件注册到MyBatis的全局配置当中,然后MyBatis执行sql映射文件中的sql语句;

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

mapper:表示注册一个sql映射文件;

配置文件注册:

resource:类路径下指定sql映射文件的位置(通常单独定义个包,里面放sql映射文件)

url:引用网络上或者是磁盘上的映射文件;

注册接口:

class:使用class引用接口注册,(注意:接口和sql映射文件必须在同一个目录下,而且名字要相同)

基于注解:

MyBatis支持注解的方式,通过在接口是指明@select等增删改查方法;

还可以使用package标签批量注册;name属性为包名;

十一,MyBatis sql映射文件中insert_update获取自增主键的值

想要获取自增主键的值,如果数据库中主键是自增的,要获取主键值,可以使用useGeneratedKeys默认值是false

注意:仅仅可以使用在insert和update有用;

 <insert id="addEmp" parameterType="com.yangzlong.mybatis.bean.Employee"
  useGeneratedKeys="true" keyProperty="id">
insert into tbl_employee (last_name,gender,email)values(#{lastName},#{gender},#{email})
 </insert>

useGeneratadKeys默认值是false,开启获取自增主键的话应该设置为true;

keyProperty指定主键映射封装到实体的哪个属性;

MyBatis底层是调用JDBC的getGenreatedkeys()方法来获取自增主键值;

####十二,MyBatis 获取非自增主键

oracle是不支持自增主键的,oracle是通过序列来模拟自增;

获取非自增主键值:

<insert id="addEmp" parameterType="com.yangzlong.mybatis.bean.Employee"
  useGeneratedKeys="true" keyProperty="id">
  <selectKey keyProperty="id" order="BEFORE" resultType="Integer">
  select tbl_employee nextval from dual
  </selectKey>
insert into tbl_employee (id,last_name,gender,email)values(#{id},#{lastName},#{gender},#{email})
 </insert>

使用selectKey来查询主键;

keyPeoperty:封装指定的对象属性;

order:在进行插入语句之前进行主键查询;

可以为after,值执行语句之后查询主键;

resultType:查询到结构的类型;

运行顺序:在进行插入语句之前查询非自增主键;

将查询到的id值封装给指定的属性;

再去运行插入语句,将封装好的id值取出来,进行插入语句的操作;

在进行插入语句之后获取主键:

<insert id="addEmp" parameterType="com.yangzlong.mybatis.bean.Employee"
  useGeneratedKeys="true" keyProperty="id" >
  <selectKey keyProperty="id" order="AFTER">
  select tbl_employee currval from dual
  </selectKey>
insert into tbl_employee (id,last_name,gender,email)
values(employe_seq.nextval,#{lastName},#{gender},#{email})
 </insert>

运行流程:

先进行插入,在序列中取到主键;

在进行selectKey主键的查询,将主键取出来;

总结:在实际开发中,用的最多的是before在插入之前取主键,after的方式是记录最后一次执行sql时的主键,所以建议使用before的方式;

十三,MyBatis映射文件参数处理,单个参数和多个参数的处理

单个参数:

public Employee getEmpById(Integer id);

单个参数MyBatis不会进行特殊的封装,#{参数名},直接可以取出数据;

 

多个参数:

public Employee getEmpIdAndLastName(@Param("id")Integer id,@Param("lastName")String lastName);

当多个参数的时候,MyBatis会进行特殊的封装,多个参数会被封装成一个map,#{}就是从map中获取指定key值,所对应的value;

key:Parma1····ParmaN;

value:就是传入的参数的值;

解决方法:

因为参数会被MyBatis进行特殊封装成一个map,会报绑定异常,底层封装是通过map(key,value)

key:param1...paramN,value就是传入参数的值,可以通过指定key来获取value的值,也可以根据下标索引的方式来指定key所对应的值;

方式一:

<select id="getEmpIdAndLastName" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee where id=#{param1} and last_name=#{param2}
</select>

方式二:

<select id="getEmpIdAndLastName" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee where id=#{0} and last_name=#{1}
 </select>

方式三:

命名参数:@param(" ")

public Employee getEmpIdAndLastName(@Param("id")Integer id,@Param("lastName")String lastName);

通过在接口方法中添加@param("指定的参数名"),来的指定参数的一一对应;也是最推介的一种方式;

十四,MyBatis映射文件 参数处理

如果在传入多个参数的时候,正好是业务中的数据模型(实体bean),我们就可以直接传入POJO;

同时也可以自定义,可以传入map集合

map(key,value)

源码分析:

 public Object getNamedParams(Object[] args) {
   final int paramCount = names.size();
   if (args == null || paramCount == 0) {
     return null;
  } else if (!hasParamAnnotation && paramCount == 1) {
     return args[names.firstKey()];
  } else {
     final Map<String, Object> param = new ParamMap<Object>();
     int i = 0;
     for (Map.Entry<Integer, String> entry : names.entrySet()) {
       param.put(entry.getValue(), args[entry.getKey()]);
       // add generic param names (param1, param2, ...)
       final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
       // ensure not to overwrite parameter named with @Param
       if (!names.containsValue(genericParamName)) {
         param.put(genericParamName, args[entry.getKey()]);
      }
       i++;
    }
     return param;
  }
}

总结:参数封装,参数多的时候MyBatis会将参数封装成一个Map,为不混乱可以使用@Param注解来指定封装时的key,#{key}就能取出map中的值;

十五,MyBatis映射文件 ${} #{}参数值得获取

区别:#{}是以预编译的形式将参数设置到sql语句中,防止了sql注入;

${}是将取出的值,直接拼接到sql语句中,会有安全问题;

在原生sql中如不需要动态获取参数的情况下:例如,进行年份分库分表之后需要传入固定的表名的时候就应该使用${}进行取值拼接操作;

十六,MyBatis映射文件 select查询语句返回List集合

public List<Employee> getListByLastNameLike(String lastName);
<select id="getListByLastNameLike" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee where last_Name like #{lastName}
 </select>

resultType:返回结果类型为返回元素的类型;并不是返回为List类型;

测试:

@Test
public void test07() throws IOException {
SqlSessionFactory sessionFactory = getSqlSessionFactory();
SqlSession session = sessionFactory.openSession();
try {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
List<Employee> list = mapper.getListByLastNameLike("%e%");
for (Employee employee : list) {
System.out.println(employee);
}
} finally {
session.close();
}
}

十七,MyBatis映射文件 select查询封装Map

对单条记录的封装,将查询出的数据封装成一个map

public Map<String,Object> getMapById(Integer id);
<select id="getMapById" resultType="map">
select * from tbl_employee where id=#{id}
 </select>

封装单条记录为map集合,resultType:返回结果类型为map类型(MyBatis已经自动封装了好类型)

测试:

    @Test
public void test08() throws IOException {
SqlSessionFactory factory = getSqlSessionFactory();
SqlSession session = factory.openSession();
try {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Map<String, Object> map = mapper.getMapById(2);
System.out.println(map);
} finally {
session.close();
}

}
{gender=1, id=2, last_Name=jreey, email=jreey@163.com}

 

多条记录进行封装:

@MapKey("id")
public Map<Integer,Employee> getMapByIdLike(String lastName);

@MapKey(“id”)根据id为列名进行查询. Map集合中key表示为列名(记录主键),value就表示为值(返回javaBean类型);

 <select id="getMapByIdLike" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_employee where last_Name like #{lastName}
 </select>

多条记录进行封装成Map,映射文件中resultType:返回值类型为javabean类型;(我们要做的是封装一个map集合,集合的value是一个JavaBean对象所以返回结果类型应该是javaBean的类型)

测试:

@Test
public void test09() throws IOException {
SqlSessionFactory factory = getSqlSessionFactory();
SqlSession session = factory.openSession();
 try {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Map<Integer, Employee> map = mapper.getMapByIdLike("%r%");
System.out.println(map);
} finally {
session.close();
}
}

{2=Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com],
3=Employee [id=3, lastName=jreey, gender=1, email=jreey@163.com],
4=Employee [id=4, lastName=jreey, gender=0, email=jreey@163.com]}

十八,MyBatis映射文件 select查询 resultMap自定义结果集映射规则

resultType:MyBatis会进行自动封装的相应的指定的javaBean类型;根据我们对其的指定自动进行封装;

resultMap:自定义封装,在没对应的JavaBean时自定义封装类型,换而言之:如果查出的数据和javaBean的属性没有进行一一对应时就可以使用resultMap自定义映射规则;

<resultMap type="com.yangzlong.mybatis.bean.Employee" id="myemp">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
 </resultMap>
 
 <select id="getEmp" resultMap="myemp">
select * from tbl_employee where id=#{id}
 </select>

type:自定义结果集的类型;

id:自定义结果集映射规则的唯一ID;

column:数据库表中的字段;

property:java实体中对应的属性;

测试:

@Test
public void test10() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee emp = mapper.getEmp(2);
System.out.println(emp);
} finally {
openSession.close();
}
}

 

十九,MyBatis映射文件,select查询,关联查询级联属性封装结果

如果业务需求中存在对象关联,可以使用ResultMap自定义封装结果集,比如数据库中存在多表的关联,在resultMap中就可以使用级联属性的方式进行自定义结果集的封装;

private Integer id;
private String lastName;
private String gender;
private String email;
private Department department;

在javaBean中存在对象关联

SELECT e.id id,e.last_name lastName,e.email email,e.gender gender,e.d_id did,d.id id,d.dept_name deptName
FROM tbl_employee e,tbl_dept d
WHERE e.d_id=d.id
AND e.id=2

数据库中进行多表关联查询

在Mapper映射文件中使用resultMap自定义封装映射结果集来对属性进行封装

<resultMap type="com.yangzlong.mybatis.bean.Employee"
id="MyEmpAndDept">
<id column="id" property="id" />
<result column="last_name" property="lastName" />
<result column="email" property="email" />
<result column="gender" property="gender" />
<result column="d_id" property="department.id" />
<result column="deptName" property="department.deptName" />
</resultMap>
<select id="getDeptAndEmpById" resultMap="MyEmpAndDept">
select e.id id,e.last_name lastName,e.email email,e.gender gender,e.d_id
did,d.id id,d.dept_name deptName
from tbl_employee e,tbl_dept d
where e.d_id=d.id
and e.id=#{id}
</select>

 

测试方法:

@Test
public void test11() throws IOException {
SqlSessionFactory sessionFactory = getSqlSessionFactory();
SqlSession session = sessionFactory.openSession();
try {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Employee id = mapper.getDeptAndEmpById(2);
System.out.println(id);
//JSONObject emp = new JSONObject(id);
//System.out.println(emp);
//int i = emp.length();
//System.out.println(i);
} finally {
session.close();
}
}

二十,MyBatis映射文件,select查询,resultMap自定义映射结果集,关联查询association定义关联对象封装规则

使用级联属性的方式可以自定义封装规则还可以使用association标签来指定联合查询;

可以使用association来关联单个对象

    <resultMap type="com.yangzlong.mybatis.bean.Employee" id="MyEmpAndDept2">
<id column="id" property="id" />
<result column="lastName" property="lastName" />
<result column="email" property="email" />
<result column="gender" property="gender" />

<association property="department" javaType="com.yangzlong.mybatis.bean.Department">
<id column="did" property="id"/>
<result column="deptName" property="deptName"/>
</association>
</resultMap>
<select id="getDeptAndEmpById" resultMap="MyEmpAndDept2">
select e.id id,e.last_name lastName,e.email email,e.gender gender,e.d_id
did,d.id id,d.dept_name deptName
from tbl_employee e,tbl_dept d
where e.d_id=d.id
and e.id=#{id}
</select>

使用association来联合查询;property:指定关联的对象属性名,JavaType:指定该对象的全类名;

 

二十一,MyBatis映射文件,select查询,resultMap自定义封装 结果集,使用association进行分步查询

上述介绍了自定义映射结果集中association的关联查询,association还可以进行分步查询,好处是:不用写复杂的多表关联sql语句,可以组合已有的方法,使用多个简单sql语句就可以实现需求,可读性高思路清晰;

DepartmentMapper.xml:

<mapper namespace="com.yangzlong.mybatis.DAO.DepartmentMapper">
<select id="getIdByDept" resultType="com.yangzlong.mybatis.bean.Department">
select id,dept_name deptName from tbl_dept where id=#{id}
</select>
</mapper>

EmployeeMapper.xml:

<resultMap type="com.yangzlong.mybatis.bean.Employee" id="myEmpStep">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<association property="dept"
select="com.yangzlong.mybatis.DAO.DepartmentMapper.getIdByDept" column="d_id">
</association>
</resultMap>

<select id="getEmpByIdStep" resultMap="myEmpStep" >
select * from tbl_employee where id=#{id}
</select>

使用association进行分步查询,property:employee中关联对象属性名称,

select:指定通过那个查询方法;

column:java实体中对象数据库字段;

思路:通过select指定的方法(插入column指定的参数值)查出对象,并封装给property指定的java属性;

测试及结果:

@Test
public void test12() throws IOException {
SqlSessionFactory sessionFactory = getSqlSessionFactory();
SqlSession session = sessionFactory.openSession();
try {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Employee step = mapper.getEmpByIdStep(2);
System.out.println(step);
} finally {
session.close();
}
}
Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com, dept=Department [id=1, deptName=开发部]]

二十二,MyBatis映射文件,select查询 基于resultMap中 association分步查询使用的延时加载(懒加载,或按需加载)

在mybatis进行分步查询时,如果只需要得到某一个单独的字段或者说一个对象时(因需求而定)使用了association的分步查询,这样的话就会进行两次查询,会访问两次数据库,既耗费了资源又浪费了时间,这时候我们就可以使用mybatist提供的延时加载,又叫懒加载;

<resultMap type="com.yangzlong.mybatis.bean.Employee" id="myEmpStep">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<association property="dept"
select="com.yangzlong.mybatis.DAO.DepartmentMapper.getIdByDept" column="d_id">
</association>
</resultMap>

<select id="getEmpByIdStep" resultMap="myEmpStep" >
select * from tbl_employee where id=#{id}
</select>

之前的配置文件不需要修该,在我们mybatis的全局配置文件中添加两个设置项就可以实现 懒加载:

<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>

lazyLoadingEnable:懒加载延迟加载开关,默认值是false,开启时所有的关联对象就会延迟加载;

aggressiveLazyLoading:默认值为false,禁止的时候我们需要的属性会按需加载,开启的话属性会全部加载;

 

####二十三,MyBatis映射文件,select查询_resultMap关联查询,collection定义关联集合封装规则

需求:查询部门的时候,将部门对应的所有员工信息查询出来; 之前的association是定义对象,现在我们要定义集合使用collection;

查部门的时候查询部门所有的员工;

    private Integer id;
private String deptName;
private List<Employee> emp;

每个不懵对应多个员工;一对多的关系;

接口方法:

public Department getDeptAndEmp(Integer id);

映射文件xml:

<resultMap type="com.yangzlong.mybatis.bean.Department" id="myDeptMap">
<id column="did" property="id"/>
<result column="dept_name" property="deptName"/>
<collection property="emp" ofType="com.yangzlong.mybatis.bean.Employee">
<id column="e_id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>

</collection>
</resultMap>

<select id="getDeptAndEmp" resultMap="myDeptMap">
SELECT
d.id did,d.dept_name dept_name,e.id e_id,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>

使用resultMap自定义结果集封装,集合使用collection来封装集合类型,

property:集合属性值;

ofType:定义集合属性的类型;

测试结果:

@Test
public void test13() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession session = sqlSessionFactory.openSession();
try {
DepartmentMapper mapper = session.getMapper(DepartmentMapper.class);
Department dept = mapper.getDeptAndEmp(2);
System.out.println(dept);
System.out.println(dept.getEmp());
} finally {
session.close();
}
}
Department [id=2, deptName=销售部, emp=[Employee [id=3, lastName=jreey, gender=1, email=jreey@163.com], Employee [id=7, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=8, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=10, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=11, lastName=tom2, gender=0, email=jreey@163.com]]]
[Employee [id=3, lastName=jreey, gender=1, email=jreey@163.com], Employee [id=7, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=8, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=10, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=11, lastName=tom2, gender=0, email=jreey@163.com]]

二十四,MyBatis映射文件 select查询_resultMap关联查询collection集合封装分步查询,延迟加载

之前使用association关联查询对象可以是使用分步查询,在使用collection封装集合的时候同样也可使用分步查询和使用延迟加载;

需求:根据部门Id查询该部门下所有的员工信息,要求使用分步查询;

查询方法:根据部门ID查询部门

public Department getDeptById(Integer deptId);

员工方法:根据部门ID查询所有的员工

public List<Employee> getEmpsByDeptId(Integer deptId);

步骤:通过部门ID查询部门信息,员工表中d_id为两表的关联外键,在员工表中查询需传入关联到部门的外键也就是部门id,即员工表中的d_id字段;

DepartmentMapper:

<resultMap type="com.yangzlong.mybatis.bean.Department" id="myDepts">
<id column="id" property="id"/>
<result column="dept_name" property="deptName"/>
<collection property="emp" select="com.yangzlong.mybatis.DAO.EmployeeMapper.getEmpByIdStep" column="id">

</collection>
</resultMap>

<select id="getDeptById" resultMap="myDepts">
select * from tbl_dept where id=#{id}
</select>

同样适用selcet属性指定方法进行参数查询(column参数指定数据库字段进行参数查询)然后封装给property指定的属性;

EmployeeMapper:

<select id="getEmpByIdStep" resultType="com.yangzlong.mybatis.bean.Employee" >
select * from tbl_employee where d_id=#{deptId}
</select>

测试结果:

Department [id=1, deptName=开发部, emp=[Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com], Employee [id=4, lastName=jreey, gender=0, email=jreey@163.com], Employee [id=5, lastName=tom, gender=0, email=jreey@163.com], Employee [id=6, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=9, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=12, lastName=tom2, gender=0, email=jreey@163.com]]]
[Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com], Employee [id=4, lastName=jreey, gender=0, email=jreey@163.com], Employee [id=5, lastName=tom, gender=0, email=jreey@163.com], Employee [id=6, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=9, lastName=tom2, gender=0, email=jreey@163.com], Employee [id=12, lastName=tom2, gender=0, email=jreey@163.com]]

二十五,MyBatis映射文件,select查询_resultMap关联查询传递多列参数值&fetchType

不管使用的是association还是collection在这两个标签中都有column属性,对应的是传递参数给select查询方法并封装给property对应的对象属性或者集合属性,同时,也可传递多个参数;

可以通过将多列参数封装成map的形式来传递,如:column="{key1=column1,key2=column2}"封装成map集合的形式;

同时可以使用fetchType=“lazy”的配置实施延迟加载;

该属性可以取两个值:lazy:开启延迟加载配置;

eager:开启立即加载;

<collection property="emp" select="com.yangzlong.mybatis.DAO.EmployeeMapper.getEmpByIdStep" column="id"
fetchType="lazy">

</collection>
<association property="department" javaType="com.yangzlong.mybatis.bean.Department" fetchType="lazy">
		<id column="did" property="id"/>
		<result column="deptName" property="deptName"/>
</association>

####26,MyBatis映射文件,select_resultMap_discriminator鉴别器

这是最后一个resultMap中的标签,鉴别器driscriminator,主要是用来进行做鉴别处理的;指定一个属性并对他进行操作,例:age属性,当年龄大于18岁可以进行贷款申请,小于18岁不可以进行贷款申请;

<resultMap type="com.yangzlong.mybatis.bean.Employee" id="MyEmpDis">
		<id column="id" property="id"/>
		<result column="last_name" property="lastName"/>
		<result column="gender" property="gender"/>
		<result column="email" property="email"/>
		<discriminator javaType="string" column="gender">
			<case value="1" resultType="com.yangzlong.mybatis.bean.Employee">
				<id column="id" property="id"/>
				<result column="last_name" property="lastName"/>
				<result column="last_name" property="gender"/>
				<result column="email" property="email"/>
			</case>
			<case value="0" resultType="com.yangzlong.mybatis.bean.Department">
				<association property="dept" 
				select="com.yangzlong.mybatis.DAO.DepartmentMapper.getIdByDept" column="d_id">	
				</association>
			</case>
		</discriminator>
	</resultMap>

使用discriminatory进行鉴别处理:javaType:为指定该字段的数据类型;column:java实体所对应的字段类型;

case:所对应的该属性的鉴别规则,如上当性别为女时查询部门,性别为男时使用姓名做email;

用的不是很多,作为了解知识;

27,Mybatis动态SQL,if判断_OGNL表达式

动态SQL和JSTL语法很相似,都是基于xml中,但是Mybatis大大简化了这一操作,只需了解之前一办多的元素就可以,而且MyBatis使用了OGNL表达式来淘汰其他元素,大大简化了操作;

if元素:通过if元素可以对sql语句进行筛选和可选,判断其是否为空是否为null;

<select id="getEmp" resultType="com.yangzlong.mybatis.bean.Employee">
select * from tbl_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.trim()!=''">
and email=#{email}
</if>
<if test="gender==0 or gender==1">
gender=#{gender}
</if>
</select>

在日常拼接sql语句时可以使用if元素对sql进行判定;如果有该字段就进行查找,没有就不查找;

@Test
public void testDynimacSQL() throws IOException {
SqlSessionFactory factory = getSqlSessionFactory();
SqlSession session = factory.openSession();
try {
EmployeeMapperDynamicSQL mapper = session.getMapper(EmployeeMapperDynamicSQL.class);
Employee employee = new Employee(2, "%e%", null, "");
List<Employee> emp = mapper.getEmp(employee);
for (Employee e : emp) {
System.out.println(e);
}
} finally {
session.close();
}

}

结果:

2019-06-26 04:15:59,196 DEBUG [com.yangzlong.mybatis.DAO.EmployeeMapperDynamicSQL.getEmp] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:145) - ==>  Preparing: select * from tbl_employee where id=? and last_name like ? 
2019-06-26 04:15:59,274 DEBUG [com.yangzlong.mybatis.DAO.EmployeeMapperDynamicSQL.getEmp] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:145) - ==> Parameters: 2(Integer), %e%(String)
2019-06-26 04:15:59,314 DEBUG [com.yangzlong.mybatis.DAO.EmployeeMapperDynamicSQL.getEmp] org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:145) - <==      Total: 1
Employee [id=2, lastName=jreey, gender=1, email=jreey@163.com]

28,MyBatis动态SQL,where查询条件

在我们进行查询的时候有些条件没带,可能在SQL拼装的时候有问题;

如:我们要根据ID查数据,但是如果ID为null的时候SQL语句如下:

Preparing: select * from tbl_employee where and last_name like ? 

XML如下:

	<select id="getEmp" resultType="com.yangzlong.mybatis.bean.Employee">
  		select * from tbl_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.trim()!=''">
  			and email=#{email}
  		</if>
  		<if test="gender==0 or gender==1">
  			gender=#{gender}
  		</if>
  	</select>

如果ID为null的时候,失去了语句拼接就会有错误,select * from tbl_employee ==where and== last_name like ? where 关键字 和and,所有解决办法:

解决方案一:

<select id="getEmp" resultType="com.yangzlong.mybatis.bean.Employee">
  		select * from tbl_employee
  		where 1=1
  		<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.trim()!=''">
  			and email=#{email}
  		</if>
  		<if test="gender==0 or gender==1">
  			gender=#{gender}
  		</if>
  	</select>

通过给where后面添加一条1=1(条件为true)的方式,可以解决SQL拼接问题的发生;

解决方案二:

<select id="getEmp" resultType="com.yangzlong.mybatis.bean.Employee">
  		select * from tbl_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.trim()!=''">
	  			and email=#{email}
	  		</if>
	  		<if test="gender==0 or gender==1">
	  			gender=#{gender}
	  		</if>
  		</where>
  	</select>

使用where标签把所有条件查询条件全部包裹住,都写在where标签中;但必须拼接条件的and必须写在语句左边;

29,MyBatis动态SQL,trim字符串截取

我们知道,在使用where查询条件时,如果拼接SQL的语句关键字and或者or写在了条件的右边(后面),就会导致SQL语句错误,执行报错,所以我们使用trim来进行字符串的截取;

只要将所有条件执行SQL写在trim标签内,就相当于就这些条件语句进行拼串操作;

<select id="getEmpTrim" resultType="com.yangzlong.mybatis.bean.Employee">
  		select * from tbl_employee
  		<trim prefix="where" suffixOverrides="and">
  			<if test="id!=null">
  				id=#{id} and
  			</if>
  			<if test="lastName!=null and lastName!=''">
  				 last_name like #{lastName} and
  			</if>
  			<if test="email!=null and email!=''">
  				email=#{email} and
  			</if>
  			<if test="gender==0 or gender==1">
  				gender=#{gender}
  			</if>
  			
  		</trim>
  	
  	</select>

prefix:前缀,给整个trim标签中的串加一个前缀;

suffix :后缀 给整个trim标签包裹的串加一个后缀;

prefixOverrides:前缀覆盖,覆盖掉trim标签中串前面的某个字符;

suffixOverrides:后缀覆盖,覆盖掉trim标签中串后面的某个字符;

30,MyBatis动态SQL,choose分支选择

choose分支就类似switch-case分支结构,when就相当于每个case;

<select id="getEmpsChoose" resultType="com.yangzlong.mybatis.bean.Employee">
  		select * from tbl_employee
  		<where>
  		<choose>
  			<when test="id!=null">
  				id=#{id}
  			</when>
  			<when test="lastName!=null and lastName!=''">
  				last_name like #{lastName}
  			</when>
  			<when test="email!=null">
  				email=#{email}
  			</when>
  			<otherwise>
  				gender=0
  			</otherwise>
  			
  		</choose>
  		</where>
  	</select>

==在choose里面包裹的就相当于带了break的switch分支语句,每一个when就相当于case,当没有对应的条件时才进otherwise;==

31,MyBatis动态SQL,set与if结合动态跟新

在进行update更新操作的时候可以配合if标签一起使用,传入哪个字段就跟新哪个字段,如果没有传入的就不进行更新操作;使用set标签可以防止SQL拼接错误,在更新的时候会有多余逗号的干扰,可以使用set标签来解决,同样的也可以使用trim标签截取多余的逗号;

//更新操作,传入一个employee对象
public void updateEmp(Employee employee);

xml映射:

<update id="updateEmp">
  		update tbl_employee
  		set
  			<if test="lastName!=null">
  				last_name=#{lastName},
  			</if>
  			<if test="email!=null">
  				email=#{email}
  			</if>
  			<if test="gender!=null">
  				gender=#{gender}
  			</if>
  			where id=#{id}
  	</update>
出现SQL错误:
 Preparing: update tbl_employee set last_name=?, where id=? 

拼接的SQL语句出现了错误,多了一个逗号,导致程序报错;

解决方法一:

使用set标签:将所有更新条件全部包裹在内看成一个整体;

<update id="updateEmp">
  		update tbl_employee
  		<set>
  			<if test="lastName!=null">
  				last_name=#{lastName},
  			</if>
  			<if test="email!=null">
  				email=#{email}
  			</if>
  			<if test="gender!=null">
  				gender=#{gender}
  			</if>
  		</set>	
  			where id=#{id}
  	</update>
==>  Preparing: update tbl_employee SET last_name=? where id=?

这样就不会出现SQL拼接的问题;

解决方法二:

使用trim标签截取字符串:使用preffx设置前缀,使用suffixOverrides覆盖语句的逗号;

    <update id="updateEmp">
<!-- update tbl_employee
<set>
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email}
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</set>
where id=#{id} -->
update tbl_employee
<trim prefix="set" suffixOverrides=",">
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email}
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</trim>
where id=#{id}
</update>
==>  Preparing: update tbl_employee set last_name=? where id=? 

同样的可以可以避免sql拼接问题;

ps:在执行更新操作的时候记得要进行commit提交操作;

32,MyBatis动态SQL,foreach遍历集合

在开发中我们会遇到,一条SQL语句会查多条数据,比如where id in(?,?,?);类似于这样的操作,这时候就要用到foreach标签了;

通过多个ID查询多条数据;

public List<Employee> getEmpsFoeach(@Param("ids")List<Integer> ids);

xml映射文件:

<select id="getEmpsFoeach" resultType="com.yangzlong.mybatis.bean.Employee">
  		select * from tbl_employee where id in
  		<foreach collection="ids" item="item_ids" separator="," open="(" close=")">
  			#{item_ids}
  		</foreach>
  	</select>

通过foreach标签来遍历list集合:collection:遍历的是那种集合,上述为list,如果是map就写map;

item:将当前遍历的值赋值给指定的变量;(接口方法传进来的参数)

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

open遍历出所有元素拼接一个开始的字符;

close:遍历出所有元素拼接一个结束字符;

index:索引;

如果是list的时候就是指的索引;item就是每个元素的值;

如果是map的时候值得就是map中的key,item就是map中的value;

查询结果:

==>  Preparing: select * from tbl_employee where id in ( ? , ? ) 

33,MyBatis动态SQL,在mysql下foreach批量插入的两种方法

在我们添加数据的时候也可以使用foreach进行批量插入数据到数据库中;

第一种:

<insert id="addEmps">
insert into tbl_employee(last_name,email,gender,d_id)
values
<foreach collection="emps" separator="," item="emp" open="(" close=")">
#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id}
</foreach>
</insert>

第二种:

<insert id="addEmps">

<foreach collection="emps" separator=";" item="emp" >
insert into tbl_employee(last_name,email,gender,d_id)
values #{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id}
</foreach>
</insert>

直接使用封号隔开,发送多条SQl语句,但前提是必须打开mysql数据库连接属性,允许发送多条SQL;t同样的方法可以进行批量删除和批量修改,仅限于mysql;

jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true

34,MyBatis动态SQL,oracle下foreach批量操作的两种方法

刚刚使用mysql数据库,进行批量操作的方法,可以使用多条语句一起发送也可以使用类似VALUES(),(),()...的方法来进行批量操作,然而oracle不支持者中连续操作方式;

oracle不支持values(),(),(),()这种方式的操作;

我们可以是使用方式一:

使用begin end 来包裹插入语句;

    begin

insert into employees (employee_id,last_name,email)values

(employee_seq.nextval,'test01','test01@162.com');

insert into employees (employee_id,last_name,email)values

(employee_seq.nextval,'test01','test01@162.com');

end
<insert id="addEmps">

`<foreach collection="emps" item="emp" open="begin" close="end;">
insert into employees (employee_id,last_name,email)values

(employee_seq.nextval,#{emp.lastName},#{emp.email});
</foreach>

</insert>

方式二:可以使用中间表来进行批量插入操作;

insert into employees(employee_id,last_name,email) 
select employees_seq.nextval,lastName,email from(
     
      select 'test01' lastName,'test01@e11.com' email from dual
      union
      select 'test02' lastName,'test02@e11.com' email from dual
     
)

使用中间表的形式;

 <insert id="addEmps">
insert into employees(employee_id,last_name,email)
<foreach collection="emps" item="emp" open="select employees_seq.nextval,lastName,email from(" close=")" separator="union">
select #{emp.lastName} lastName,#{emp.email} email from dual
</foreach>
</insert>

35,MyBatis动态SQL,内置参数_parameter & _databaseId

MyBatis中内置了两个参数,

_paramter:当传进来是一个参数的时候parameter就代表这个参数,如果说传进来是多个参数时,mybatis会进行参数封装,将参数封装成一个map集合,此时这个集合就是parameter;

_databaseId:如果配置databaseprvoder,这个参数就产生了,databaseId就代表者配置数据库的别名;

例:


<select id="getEmpss" resultType="com.yangzlong.mybatis.bean.Employee">
<if test="_databaseId=='oracle'">
select * from employees
</if>
<if test="_databaseId=='mysql'">
select * from tbl_employee
</if>
</select>

根据不同数据库_databaseId来分别执行不同sql语句到不同数据库;

例:

 <select id="getEmpss" resultType="com.yangzlong.mybatis.bean.Employee">
<if test="_databaseId=='oracle'">
select * from employees
<if test="_parameter !=null">
where last_name=#{lastName}
</if>
</if>
<if test="_databaseId=='mysql'">
select * from tbl_employee
</if>
</select>

根据_parameter内置参数来判断传进来的参数,此时的parameter就相当于传进来的employee,也可以写写成

#{_parameter.lastName};

36,MyBatis动态SQL,绑定_bind

bind绑定参数操作;我们要进行模糊查询的时候,要求查询%e%名字中带有e的员工;

    emp.setLastName("e");

不在代码中直接写“%e%”可以在mapper中直接通过bind来绑定到参数中;

<select id="getEmpss" resultType="com.yangzlong.mybatis.bean.Employee">
<if test="_databaseId=='oracle'">
<bind name="_lastname" value="'%'+lastName+'%'"/>
select * from employees
<if test="_parameter !=null">
where last_name like #{_lastname}
</if>
</if>
<if test="_databaseId=='mysql'">
select * from tbl_employee
</if>
</select>

bind可以通过OGNL表达式的值来绑定到一个变量中,然后引用,<bind name="_lastname" value="'%'+lastName+'%'"/> 引用到#{_lastname};

37,MyBatis动态SQL,抽取可重用的SQL片段

可以将一些重复使用的字段全部提取出来

 <sql id="selectClom">
   <!--同样的也可以进行判断-->
  	 	<if test="_databaseId=='mysql'">
	  	 	last_name,gender,email,d_id
  	 	</if>
  	 	<if test="_databaseId=='oracle'">
  	 		employee_id,last_name,email
  	 	</if>
  	 </sql>

引用:

<select id="getEmpss" resultType="com.yangzlong.mybatis.bean.Employee">
  	 	<if test="_databaseId=='oracle'">
  	 	<bind name="_lastname" value="'%'+lastName+'%'"/>
  	 		select 
  	 			<include refid="selectClom"/>
  	 		 from employees 
  	 		<if test="_parameter !=null">
  	 			where last_name like #{_lastname}
  	 		</if>
  	 	</if>
  	 	<if test="_databaseId=='mysql'">
  	 		select 
  	 			<include refid="selectClom"/>
  	 		 from tbl_employee
  	 	</if>
  	 </select>

38,MyBatis缓存,介绍

MyBatis有两级缓存;

一级缓存:sqlsession,只要是同一个sqlsession对象就会自动启动一级缓存机制;sqlsession本身死一个map如果执行操作,会存在这个map中,再次执行的时候会先从map中查询,有就直接拿,没有就查询数据库;

缓存失效:1,不同的sqlsession;

2,sqlsession相同,查询条件不同;

3,sqlSession相同,在此之前执行了增删改操作可能对该数据有影响;

4,sqlSession相同,手动清除了一级缓存(clearCahe);

二级缓存:全局缓存,基于namespace级别的缓存,一个namespace对应一个二级缓存;

工作机制:1,一个会话查询一条数据,这个数据就会放到一级缓存中去,

2,如果会话关闭,数据会放到二级缓存中,新的会话查询就会参照一级缓存;

3,不同namespac查出来的数据会放在不同的自己对应的缓存中;(map)

####39,MyBatis缓存,二级缓存的使用

开启全局缓存;

MyBatis缓存<setting name="cacheEnable" value="true"/>

xml:

 <!--
  	eviction:缓存回收策略;
  	flushInterval:缓存刷新间隔的时间默认毫秒
  	readOnly:是否只读;
  		true:只读,速度快,不安全;
  		false:非只读;都有可能被修改;速度慢,安全
  	size:存放缓存的个数;
    -->
  	<cache eviction="FIFO" flushInterval="6000" readOnly="false" size="1024">
  	
  	</cache>

 

####40,MyBatis缓存,缓存的有关设置和属性

<setting name="cacheEnable" value="true"/>

*该设置是开启二级缓存;一级缓存是一直可用的;

*每个select标签都会有一个属性usercahe=“ true”;默认的是开启缓存,如果设置为false,一级缓存可以使用,二级缓存就会关闭;

*在使用一级缓存的时候,在同样的sqlSession的时候只要执行一次增删改操作,一级缓存就失效了,这是因为,在每个增删改标签上都有一个默认的属性:flashcahe=“true”,他会自动执行完后刷新缓存;清除掉缓存;同样的二级缓存也会被清除掉;在查询标签中flashcahe是false,如果改为true,每次查询都会清除,缓存就没有被使用到的;==增删改后一级二级都会清除==;

*opensession.clearCache()方法只会清除当前session的一级缓存;和二级缓存是没干系的;

*localcachescope:本地缓存作用域,SESSION|STATEMENT两个值,session就是一级缓存默认值;statement的时候禁用一级缓存;

41,MyBatis的工作流程

MyBatis框架主要分为以下几大层面;

==引导层==:基于xml配置;基于javaAPI;

==框架支持层==:主要有全局配置文件;以及配置方式:基于xml配置,基于注解配置;

==数据处理层==:参数映射;参数解析;SQL执行;结果处理和映射;

==接口层==:接口的处理方式:基于statementID;基于mapper接口

数据增加接口;数据删除接口;数据修改接口;数据查询接口;数据维护接口;

 

==数据处理层==:参数映射;参数解析;SQL执行;结果处理和映射;

==接口层==:接口的处理方式:基于statementID;基于mapper接口

数据增加接口;数据删除接口;数据修改接口;数据查询接口;数据维护接口;

 

posted on 2019-10-03 17:00  y-zlong  阅读(666)  评论(0编辑  收藏  举报

导航