mybatis
关于mybatis可能用到的技术:
①mybatis ② mybatis-spring ③MyBatis Generator
优秀的分页插件 PageHelper
起步1–之前的mybatis写法
-
工程目录
-
所用工具eclipse,MySQL,Mybatis版本3.5.6
-
所用jar包:mybatis自带的jar包,连接数据库需要的jar包,日志包
-
根据官方文档 进行项目的启动
数据库中表employee的准备
编写实体类,实体类中有get(),set(),toString(),无参,有参构造器的方法
- 编写mybatis-config.xml的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/girls" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!-- 全局配置,需要映射 sql语句的xml配置文件 -->
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
</configuration>
- log4j.properties的配置文件
log4j.rootLogger=debug, stdout, logfile
log4j.logger.com.ibatis=DEBUG
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.org.springframework=ERROR
log4j.logger.org.compass=ERROR
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=empi.log
log4j.appender.logfile.MaxFileSize=512KB
log4j.appender.logfile.MaxBackupIndex=3
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
- EmployeeMapper.xml的编写
<?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.hyq.EmployeeMapper">
<!-- id:唯一标识,resultType:返回的结果类型,这里是返回一个Employee实体 -->
<select id="selectEmp" resultType="com.hyq.entity.Employee">
select id,last_name lastName,gender,email
from employee
where id = #{id}
</select>
</mapper>
- 4.编写测试类
public static void main(String[] args) throws Exception {
//根据xml配置文件创建一个sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
//从sqlSessionFactory中获取一个sqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
/**
<T> T selectOne(String statement, Object parameter);
statement Unique identifier matching the statement to use.
parameter A parameter object to pass to the statement.
*/
//这里的selectEmp对应的就是EmployeeMapper.xml中的id
Employee employee = session.selectOne("selectEmp", 1);
System.out.println(employee);
}
}
起步2–接口式编程写法
- 编写一个接口EmployeeMapper
public interface EmployeeMapper {
//查询某个员工
Employee getEmployeeById(int id);
}
- 修改EmployeeMapper.xml中的namespace和id
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 这里的namespace对应的是接口的全限定类名 -->
<mapper namespace="com.hyq.dao.EmployeeMapper">
<!-- 这里的id对应的该接口中的方法名称 -->
<select id="getEmployeeById" resultType="com.hyq.entity.Employee">
<!-- 这里的lastName要和JavaBean(这里是Employee)中字段一致才能查出具体值,要是不一致,查出的值是空值 -->
select id,last_name lastName,gender,email
from employee
where id = #{id}
</select>
</mapper>
- 测试类用接口来操作Sql statement
public static void main(String[] args) throws Exception {
//根据xml配置文件创建一个sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
//从sqlSessionFactory中获取一个sqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class); //EmployeeMapper是接口
//输出:org.apache.ibatis.binding.MapperProxy@3159c4b8
//这是一个代理的对象
System.out.println(employeeMapper);
Employee employee = employeeMapper.getEmployeeById(1);
System.out.println(employee);
}
}
mybatis的配置
properties
properties中有两个标签url 和 resource
url:加载非类路径下的文件,如磁盘中文件
resource属性是按照类路径的写法来写的,因此必须存在于类路径下
- 编写jdbc.properties的文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/girls
jdbc.username=root
jdbc.password=123456
- mybatis-config.xml配置文件中需要引入外部配置文件
项目结构
settings
mapUnderscoreToCamelCase
是否开启驼峰命名。默认不开启。若开启,则数据库中假如有一个字段a_name,它会将该字段映射为aName,此时你的JavaBean中若有一个字段是aName,映射的aName会和JavaBean的属性aName自动关联起来。防止有时候出现空值。例如:数据库中字段是a_age,在JavaBean中有一个属性值aAge,若没有开启驼峰命名,则查到的值是空值,当然你可以开启驼峰命名或者是 给 该字段起个别名即可。
typeAliases
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写
- 1.可以为某个Java类起个别名。如:
<typeAliases>
<!-- 起别名后,在别的地方不用写全类名,默认 Java类 首字母小写即可 -->
<typeAlias type="com.hyq.entity.Employee" />
</typeAliases>
- 2.当某个JavaBean包下有多个 Javabean时,为每个包起别名 会出现代码冗余,可以通过设置包的形式为每个Java类 起别名
<typeAliases>
<package name="com.hyq.entity"/>
</typeAliases>
- 3.当 javabean包下有重名的类时,可以通过注解@Alias
- 4.当Java类上存在@Alias注解时,注解生效,别名就是注解值,mybatis-config.xml中的别名配置不起作用
- 5.mybatis中内置了一些基本数据类型,包装类,日期类,集合类的别名
mappers(这里项目结构发生了变化,这里的项目结构是常用类型,之后的都以此为准)
项目目录结构
说明:conf和lib目录是自己创建的文件夹,刚开始创建的文件夹和lib文件夹类型一样,这里要让conf文件夹成为资源目录(如下图),在conf下右键–>Build Path --> Use as Source Folder 后,该conf目录形状就变为了和src一样的标志。使用Java代码加载类路径下的配置文件,就不会出现找不到文件的错误。
该项目结构中的代码,配置还和上文一致,这里不再赘述
Mappers
的使用:我们需要告诉 MyBatis
到哪里去找到SQL
的映射文件。
- 1.使用相对于类路径的资源引用
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
- 2.完全限定资源定位符(包括 file:/// 形式的 URL)
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<!-- 这里是根据绝对路径来的。注意:这里必须以file:///开头 -->
<mapper url="file:///E:/CodeMgr/eclipse/mybatis_02_config/conf/EmployeeMapper.xml"/>
</mappers>
- 3.使用映射器接口实现类的完全限定类名
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="com.hyq.mapper.EmployeeMapper"/>
</mappers>
- 4.将包内的映射器接口实现全部注册为映射器
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
对于xml配置文件和XXXMapper.java接口(针对3和4)还有一种操作如下
总结:mybatis-config.xml配置文件 标签有顺序,需要按照顺序来编写
实现简单的增删改查
编写employeeMapper.java接口
编写EmployeeMapper.xml配置文件
编写测试类
public void test1() throws IOException {
//根据xml配置文件创建一个sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
//从sqlSessionFactory中获取一个sqlSession
SqlSession session = null;
try {
session = sqlSessionFactory.openSession();
EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
//插入
Employee e = new Employee(1,"tom",0,"tom1@qq.com");
// employeeMapper.addEmp(e);
//修改
// employeeMapper.updEmp(e);
employeeMapper.delEmpById(2);
//提交 ///////////////////
session.commit();
}finally {
session.close();
}
}
插入时获取自增主键的 值
useGeneratedKeys
:这会令MyBatis
使用 JDBC
的getGeneratedKeys
方法来取出由数据库内部生成的主键
keyProperty
:MyBatis
会使用 getGeneratedKeys
的返回值或 insert 语句的 selectKey
子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称.In a word
:mybatis
在得到主键的值后,会把这个值封装给JavaBean
的某个变量(keyProperty="?"
,?
里对应的是JavaBean
变量)
为插入语句加入上述两个标签
mybatis接口中参数规则
- 1.当接口中的参数只有一个时,mybatis不做处理
即:取值方式直接用 #{参数名}。而且参数名随便写 - 2.当接口中的参数有多个时,如果还写#{参数名} 就会报错
Employee getEmployeeById(int id,String lastName);
这时候xml配置文件中如果还用#{id} #{lastName}
取值,就会出现异常
原因是: 多个参数时,将参数封装成 一个Map,map中key就是param1,param2…,map中value就是具体值,这时候如果用#{id} #{lastName}
当然是找不到的,可以用#{param1} #{param2},或者是#{0},#{1}
,但是这种可读性比较差,我们有另外一种操作,就是在参数位置加上注解@param()
的方式
Employee getEmployeeById(@Param("id") int id,@Param("lastName") String lastName);
这时候就可以在xml的配置文件中用#{id},#{lastName}的方式来取值
<select id="getEmployeeById" resultType="com.hyq.entity.Employee">
select id,last_name lastName,gender,email
from employee
where id = #{id} and #{lastName}
</select>
- 3.参数是实体类
pojo
时,取值直接用#{实体类中变量名}即可 - 4.参数过多时,可以用
Map
传参,取值时就用#{map的key来取值}
Map map = new HashMap();
map.put("id",1);
map.put("lastName","tom");
//对于xml的取值,我们可以直接用#{id},#{lastName}来取值
- 4.对于集合
Collection List
数组Array
类型的参数,mybatis
作另外的处理
仍然使用Map
封装,但是Map
的key
是collection/list/array
常用参数处理
Employee getEmployeeById(int id);
//取参:#{id} #{随便写}
Employee getEmployeeById(int id,String lastName);
//取参:#{0} #{1} / #{param1} #{param2}
Employee getEmployeeById(int id,Employee employee);
//取参:#{0} #{param2.lastName}
Employee getEmployeeById(int id,@param("e") Employee e);
//取参:#{0} #{e.lastName}
Employee getEmployeeById(List<Integer> ids);
//取出第一个参数:#{list[0]}
#{}和${}的区别
#{}:是以预编译的形式,将参数设置到sql语句中,有校防止预编译
${}: 直接将参数 放在了 SQL语句中,存在sql注入问题
返回的结果是List/Map时的处理
返回结果是list
当返回值类型是List时,resultType并不是写java.util.List,而是写元素类型
编写接口中的方法
List<Employee> getEmpList();
*
编写xml 的SQL映射
<!-- 如:这里的resultType写的是List<Employee> 中元素Employee的全限定类名 -->
<select id="getEmpList" resultType="com.hyq.entity.Employee">
select id,last_name lastName,gender,email
from employee
</select>
编写测试类
结果:
[Employee [id=1, lastName=tom, gender=0, email=tom1@qq.com],
Employee [id=3, lastName=tom, gender=0, email=tom1@qq.com],
Employee [id=4, lastName=tom, gender=0, email=tom1@qq.com]
]
返回结果是Map
- 1.当返回结果是1条记录
Map<String,Object> getEmpMap(int id);
xml配置
<!-- 这里的resultMap是map-->
<select id="getEmpMap" resultType="map">
select id,last_name lastName,gender,email
from employee where id=#{id}
</select>
结果:
{lastName=tom, gender=0, id=1, email=tom1@qq.com}
- 2.返回结果是多条记录
<!-- 这里的resultType是map中value的元素类型-->
<select id="getEmpMap" resultType="com.hyq.entity.Employee">
select id,last_name lastName,gender,email
from employee where last_name like #{lastName}
</select>
结果
{1=Employee [id=1, lastName=tom, gender=0, email=tom1@qq.com],
3=Employee [id=3, lastName=tom, gender=0, email=tom1@qq.com],
4=Employee [id=4, lastName=tom, gender=0, email=tom1@qq.com]
}
自定义结果集resultMap的使用
association的使用
编写Employee类和Deptment类,存在get/set/toString/无参/有参构造器的方法
编写EmployeeMapperPlus接口方法
public interface EmployeeMapperPlus {
List<Employee> getEmployeeById();
}
编写EmployeeMapperPlus.xml的配置SQL映射
association元素是处理“有一个”类型的关系,如上面的Employee实体类的Deptment属性(一个员工只能从属于一个部门(员工只能“有一个”部门))
<!-- 方式一:级联映射-->
<!--resultMap中的id 是唯一标识,在下面select中resultMap使用了-->
<resultMap type="com.hyq.entity.Employee" id="emp">
<id column="id" property="id"/>
<!--column:数据库中字段对应的列property:Java实体类中的属性-->
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<result column="id" property="dept.id"/>
<result column="dept_name" property="dept.deptName"/>
</resultMap>
<!--方式二:关联(association)映射-->
<!--方式一/方式二写哪个都行-->
<resultMap type="com.hyq.entity.Employee" id="emp">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<!--association可以联合Javabean对象,javaType:指定property中dept的对象类型-->
<!--association中的property对应的Java中的属性-->
<association property="dept" javaType="com.hyq.entity.Deptment">
<id column="id" property="id"/>
<result column="dept_name" property="deptName"/>
</association>
</resultMap>
<!--最好先在数据库中查询一下,看能不能查询成功,确认无误在往xml中写-->
<select id="getEmployeeById" resultMap="emp">
<!--e.id:是因为employee表中有id,department表中也有id,要区分一下-->
select e.id,last_name,gender,email,dept_name
from employee e,department d
where e.id = d.id
</select>
编写mybatis-config.xml的配置,将SQL映射加入到mybatis的配置文件中去
<?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>
<!--引入外部jdbc.properties的配置文件-->
<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>
<!-- 全局配置,需要映射 sql语句的xml配置文件 -->
<mappers>
<!--配置包扫描-->
<package name="com.hyq.mapper" />
</mappers>
</configuration>
编写测试类
public void test2() throws Exception {
//根据xml配置文件创建一个sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
//从sqlSessionFactory中获取一个sqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
EmployeeMapperPlus employeeMapperPlus = session.getMapper(EmployeeMapperPlus.class);
System.out.println(employeeMapperPlus);
List<Employee> employee = employeeMapperPlus.getEmployeeById();
System.out.println(employee);
}
}
[Employee [id=1, lastName=tom1, gender=0, email=tom1@qq.com, dept=Deptment [id=1, deptName=a部, employee=null]],
Employee [id=2, lastName=tom2, gender=1, email=tom2@qq.com, dept=Deptment [id=2, deptName=b部, employee=null]] ]
collection的使用
collection处理“有多个”类型的关系,如上面的Deptment类中的List< Employee > 表示一个部门可以有多个员工
编写DeptMapper接口中的方法
List<Deptment> getAll();
编写DeptMapper.xml 实现SQL映射
<resultMap type="com.hyq.entity.Deptment" id="dept">
<id column="id" property="id"/>
<result column="dept_name" property="deptName"/>
<!-- 处理一个部门对应多个员工的情况 -->
<!-- ofType: 用来指定property="employee"中employee(和Deptment中的List< Employee > employee属性名称一致)的Java类型 -->
<collection property="employee" ofType="com.hyq.entity.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
</collection>
</resultMap>
<select id="getAll" resultMap="dept">
SELECT d.id,dept_name,last_name,gender,email
FROM department d,employee e
WHERE d.id = e.dept_id
</select>
结果
[Deptment [id=1, deptName=a部, employee=[Employee [id=1, lastName=tom1, gender=0, email=tom1@qq.com, dept=null]]],
Deptment [id=2, deptName=b部, employee=[Employee [id=2, lastName=tom2, gender=1, email=tom2@qq.com, dept=null]]]]
动态SQL的使用
小案例
编写DynamicSqlMapper接口中的方法
//通过模糊查询
List<Employee> getEmpBySql(Employee employee);
编写DynamicSqlMapper.xml实现sql映射,注意要将该xml 映射到全局mybatis-config.xml的配置文件中去,具体操作和上面一样
<?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.hyq.mapper.DynamicSqlMapper">
<select id="getEmpBySql" resultType="com.hyq.entity.Employee">
select id,last_name lastName,email,gender,dept_id
from employee
<where>
<if test="lastName!=null">
<!-- MySQL不允许where后使用别名,别名是查询结果才显示的。
因为根据MySQL中from,select等的执行顺序表明
在执行where时,别名还不存在 -->
last_name like #{lastName}
</if>
<if test="email!=null">
and email like #{email}
</if>
</where>
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 引入外部配置文件 -->
<properties resource="jdbc.properties"></properties>
<!-- <settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings> -->
<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>
<!-- 全局配置,需要映射 sql语句的xml配置文件 -->
<mappers>
<!--通过包扫描的方式,扫描mapper包下所有xml-->
<package name="com.hyq.mapper" />
</mappers>
</configuration>
编写测试类
public void test() throws Exception {
//根据xml配置文件创建一个sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
//从sqlSessionFactory中获取一个sqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
DynamicSqlMapper e = session.getMapper(DynamicSqlMapper.class);
System.out.println(e);
//第一种测试
Employee ep = new Employee(1,"%t%",0,"",null);
//第二种测试
Employee ep = new Employee(1,"%1%",0,"%qq%",null);
List<Employee> employees = e.getEmpBySql(ep);
System.out.println(employees);
}
}
输出结果
//1. SQL执行的语句:主要看where执行条件
==> Preparing: select id,last_name lastName,email,gender,dept_id from employee WHERE last_name like ? and email like ?
==> Parameters: %t%(String), (String)
[] 空值
//2. SQL执行的语句
==> Preparing: select id,last_name lastName,email,gender,dept_id from employee WHERE last_name like ? and email like ?
==> Parameters: %1%(String), %qq%(String)
[Employee [id=1, lastName=tom1, gender=0, email=tom1@qq.com, dept=null]]
choose-when-otherwise /trim的操作看官网即可
mybatis的官网
foreach用法
你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。
①当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。
②当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
- 1.集合的遍历
编写DynamicSqlMapper接口中的方法
List<Employee> getEmpBatch(List<Integer> ids);
- 2.编写DynamicSqlMapper.xml 实现SQL的映射
<select id="getEmpBatch" resultType="com.hyq.entity.Employee">
select id,last_name lastName,email,gender,dept_id
from employee
where id in
<!-- where id in(1,2,3) -->
<foreach collection="list" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
- 3.编写测试类
@Test
public void test1() throws Exception {
//根据xml配置文件创建一个sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
//从sqlSessionFactory中获取一个sqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
DynamicSqlMapper e = session.getMapper(DynamicSqlMapper.class);
System.out.println(e);
List<Employee> employees = e.getEmpBatch(Arrays.asList(1,2,3));
System.out.println(employees);
}
}
结果
//SQL语句
==> Preparing: select id,last_name lastName,email,gender,dept_id from employee where id in ( ? , ? , ? )
==> Parameters: 1(Integer), 2(Integer), 3(Integer)
[Employee [id=1, lastName=tom1, gender=0, email=tom1@qq.com, dept=null], Employee [id=2, lastName=tom2, gender=1, email=tom2@qq.com, dept=null], Employee [id=3, lastName=tom3, gender=0, email=tom3@qq.com, dept=null]]
- 2.批量添加
编写DynamicSqlMapper接口中的方法
void addBatch(List<Employee> employees);
编写DynamicSqlMapper.xml 映射sql语句
<select id="addBatch">
insert into employee(last_name,gender,email,dept_id) values
<!-- (),(),() -->
<!-- separator=,是加在了foreach里面的语句后面 -->
<foreach collection="list" item="emp" separator=",">
(#{emp.lastName},#{emp.gender},#{emp.email},#{emp.dept.id})
</foreach>
</select>
编写测试类
@Test
public void test1() throws Exception {
//根据xml配置文件创建一个sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
//从sqlSessionFactory中获取一个sqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
DynamicSqlMapper e = session.getMapper(DynamicSqlMapper.class);
System.out.println(e);
List<Employee> list = new ArrayList<Employee>();
list.add(new Employee(null,"xiaoming1",0,"xiaoming1@qq.com",new Deptment(1)));
list.add(new Employee(null,"xiaoming2",1,"xiaoming2@qq.com",new Deptment(2)));
list.add(new Employee(null,"xiaoming3",0,"xiaoming3@qq.com",new Deptment(1)));
e.addBatch(list);
}
}
查看sql语句:
==> Preparing:
insert into employee(last_name,gender,email,dept_id) values (?,?,?,?) , (?,?,?,?) , (?,?,?,?)
==> Parameters:
xiaoming1(String), 0(Integer), xiaoming1@qq.com(String), 1(Integer),
xiaoming2(String), 1(Integer), xiaoming2@qq.com(String), 2(Integer),
xiaoming3(String), 0(Integer), xiaoming3@qq.com(String), 1(Integer)
缓存概念
- 一级缓存(默认开启状态)
在同一次会话(一个sqlSession就是一个会话)期间查询到的数据会放在一级缓存中,同一个会话中有第二次相同操作的查询的话,会直接从缓存中拿数据,不在对数据库进行查询。每一个sqlSession都会放在Map中
一级缓存不起作用的四种情况
①sqlSession不同
②sqlSession相同,查询条件不同
③sqlSession相同,两次查询之间有增删改操作
④sqlSession相同,手动清除了一级缓存 - 二级缓存
基于xml中的namespace级别的缓存机制,也就是说基于一个xml文件的缓存,一个namespace对应一个二级缓存
一次会话会放入一级缓存中,只有当这个会话关闭,才会将一级缓存的数据放入二级缓存中去
步骤:
①mybatis-config.xml全局配置中需要开启缓存
②在XXXMapper.xml中开启缓存
③PoJo有时需要实现序列化接口
有一个比较成熟的第三方的缓存工具,如redis,ehcache
mybatis原理刨析
框架的结构
找一个测试类观察观察,先不用管xml,就单纯的看测试类,看看流程怎么走
public static void main(String[] args) throws Exception {
//根据xml配置文件创建一个sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
//从sqlSessionFactory中获取一个sqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
System.out.println(employeeMapper);
Employee employee = employeeMapper.getEmployeeById(1);
System.out.println(employee);
}
}
从上面测试类我们能发现:
1.先拿取一个SqlSessionFactory 工厂
2.通过SqlSessionFactory 得到一个SqlSession
3.通过SqlSession.getMapper()加载接口,这里是接口的代理对象
4.代理对象来帮我们执行增删改查方法
mybatis的四大对象
①StatementHandler(prepare,parameterize,batch,update,query)
②ParameterHandler(getParameterObject,setParameters)
③ResultHandler(handleResultSets,handleOutputParameters)
④Executor(update,query,flushStatments,commit,rollback,getTransaction,close,isClosed)
程序如何获取SqlSessionFactory
//处理这个完整的语句,最后并把所有值放入mappedStatement
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// Include Fragments before parsing
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String resultType = context.getStringAttribute("resultType");
Class<?> resultTypeClass = resolveClass(resultType);
String resultMap = context.getStringAttribute("resultMap");
String resultSetType = context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
if (resultSetTypeEnum == null) {
resultSetTypeEnum = configuration.getDefaultResultSetType();
}
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
String resultSets = context.getStringAttribute("resultSets");
//一个增删改查标签对应一个mappedStatment
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
程序如何获取SqlSession
程序如何得到的代理对象
configuration已经有了很多数据