mybatis是支持普通SQl查询,存储过程和高级映射的优秀持久层框架,半自动ORM框架。
本质
mybatis的本质就是解决Java和mysql之间的协调作用,Java通过面向对象的方式去操作mysql。
添加依赖
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
添加配置文件 mybatis-config.xml
这个配置文件中包含了MyBatis系统的核心设置;
这些mapper的XML文件包含了SQL代码和映射定义信息;
用来连接数据库
mybatis-config.xml
<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/test"/>
<property name="username" value="root"/>
<property name="password" value="westos"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="mapper/StudentMapper.xml"/>
mappers>
configuration>
提供一个 映射文件
用来管理sql语句,描述了sql语句跟数据库之间的映射关系;
mapper/StudentMapper.xml
<mapper namespace="mapper.StudentMapper">
<insert id="abc" parameterType="domain.Student"
useGeneratedKeys="true" keyProperty="sid">
insert into student(sid,sname,birthday,sex)
values (null ,#{sname},#{birthday},#{sex})
insert>
<delete id="delete" parameterType="int">
delete from student where sid= #{sid}
delete>
<select id="select" resultType="domain.Student">
select sid,sname,birthday,sex from student
select>
<select id="selectById" resultType="domain.Student" parameterType="int">
select * from student where sid=#{sid}
select>
<select id="update" parameterType="domain.Student">
update student
<set>
<if test="sname!=null">
sname=#{sname},
if>
<if test="birthday!=null">
birthday=#{birthday},
if>
<if test="sex!=null">
sex=#{sex}
if>
set>
where sid=#{sid}
select>
<select id="findByPage" parameterType="map" resultType="domain.Student">
select * from student limit #{m},#{n}
select>
mapper>
调用mybatis api使用映射文件真正执行增删改查
每一个MyBatis的应用都是以一个SqlSessionFactory的实例为中心的,SqlSessionFactory的实例可以通过SqlSessionFactoryBuilder获得,而SqlSessionFactoryBuilder则可以从XML配置文件或一个预先定制的Configuration的实力构建出SqlSessionFactory的实例;
public class TestStudentMapper {
static SqlSessionFactory factory;
static {
try {
//读取配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//创建sqlSession工厂类
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
//读取配置文件
public void testInsert() throws IOException {
//创建sqlSession ,这里的session更类似于jdbc中的Connection
SqlSession sqlSession = factory.openSession();
//执行新增
Student stu = new Student();
stu.setSname("小林");
stu.setBirthday(new Date());
stu.setSex("女");
//参数一:namespace+sql_id 参数二:传递sql语句需要的java对象
sqlSession.insert("mapper.StudentMapper.abc",stu);
//执行增删改 没有启用自动提交事务
sqlSession.commit();
//关闭资源
sqlSession.close();
}
@Test
public void testDelete(){
SqlSession sqlSession = factory.openSession();
sqlSession.delete("mapper.StudentMapper.delete",1009);
sqlSession.commit();
sqlSession.close();
}
@Test
public void testFindAll(){
SqlSession sqlSession = factory.openSession();
List<Student> stu = sqlSession.selectList("mapper.StudentMapper.select");
sqlSession.close();
for (Student student : stu) {
System.out.println(student);
}
}
@Test
public void testFindById(){
SqlSession sqlSession = factory.openSession();
Student stu = sqlSession.selectOne("mapper.StudentMapper.selectById",1002);
sqlSession.close();
System.out.println(stu);
}
@Test
public void testUpdate(){
SqlSession sqlSession = factory.openSession();
Student student = new Student();
student.setSid(1008);
student.setSex("女");
sqlSession.update("mapper.StudentMapper.update",student);
sqlSession.commit();
sqlSession.close();
}
@Test
public void findByPage(){
SqlSession sqlSession = factory.openSession();
HashMap<String, Integer> map = new HashMap<>();
map.put("m",0);
map.put("n",6);
List<Student> list = sqlSession.selectList("mapper.StudentMapper.findByPage",map);
sqlSession.close();
for (Student student : list) {
System.out.println(student);
}
}
}
通过日志工具监控Mybatis生成的sql语句
logback
添加依赖
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
添加一个logback.xml到resources文件夹;
<configuration
xmlns="http://ch.qos.logback/xml/ns/logback"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback logback.xsd">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss} [%-5level] %logger{32} - %m%n pattern>
encoder>
appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logFile.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logFile.%d{yyyy-MM-dd}.logfileNamePattern>
<maxHistory>15maxHistory>
rollingPolicy>
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss} [%-5level] %logger{17} - %m%n pattern>
encoder>
appender>
<logger name="mapper.StudentMapper" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
logger>
<root level="ERROR">
<appender-ref ref="STDOUT"/>
root>
configuration>
<delete id="deleteByIds" parameterType="list">
delete from student where sid in
<foreach collection="list" item="x" open="(" close=")" separator=",">
#{x}
foreach>
delete>
案例2:给定多个条件进行查询
用来抵消多余and带来的影响;
<select id="findByCondition" parameterType="map" resultType="domain.Student">
select sid,sname,birthday,sex from student
where 1=1
<if test="sname!=null">
and sname=#{sname}
if>
<if test="birthday!=null">
and birthday=#{birthday}
if>
<if test="sex!=null">
and sex=#{sex}
if>
select>
where标签去除多余的and;
<select id="findByCondition" parameterType="map" resultType="domain.Student">
select sid,sname,birthday,sex from student
<where>
<if test="sname!=null">
and sname=#{sname}
if>
<if test="birthday!=null">
and birthday=#{birthday}
if>
<if test="sex!=null">
and sex=#{sex}
if>
where>
select>
<select id="findByLt" parameterType="int" resultType="domain.Student">
select>
配置文件中
<mappers>
<mapper class="mapper.StudentMapper"/>
mappers>
@Insert 包括 @option
@Update
@Delete
@select
案例:
package mapper;
import domain.Student;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.Map;
public interface StudentMapper {
@Insert("insert into student(sid,sname,birthday,sex) values(null,#{sname},#{birthday},#{sex})")
@Options(useGeneratedKeys = true,keyProperty = "sid")
void insert(Student student);
//需要动态生成sql语句
void update(Student student);
@Delete("delete from student where sid=#{sid}")
void delete(int sid);
@Select("select * from student")
List<Student> findAll();
@Select("select * from studnet where sid=#{sid}")
Student findById(int sid);
@Select("select * from student limit #{m} #{n}")
List<Student> findByPage(Map map);
}
测试类
public class StudentMapperTest {
static SqlSessionFactory factory;
static {
try {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void testInsert() {
SqlSession sqlSession = factory.openSession();
//获取接口
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student stu = new Student();
stu.setSname("小林");
stu.setBirthday(new Date());
stu.setSex("女");
mapper.insert(stu);
sqlSession.commit();
sqlSession.close();
}
}
原理:
mapper的类型,这个类是使用了jdk的动态代理技术生成的类,在代码运行期间生成;所以这个接口在调用mapper时在底层被实现;
public class $Proxy10 imlements StudentMapper{
private Sqlsession sqlsessionl
public $proxy10(Sqlsession sqlsession){
this.sqlSession=sqlsession;
}
//其中sql语句从@Insert获得,参数对象就是student
public void insert(Student student){
sqlSession.insert(sql,参数对象)
}
}
缺点
接口方法不能直接应用多个方法参数;
解决方法:
1) 使用map集合来传递参数,每个参数对应map中的一个键值对;
2 ) 用@Param注解
Mapper接口中不能有方法的重载;
使用Mapper接口方式实现动态方式比较复杂;接口和xml文件结合使用;
案例:动态更新数据库
在接口中定义方法,因为需要使用xml,所以不用注解;
void update(Student student);
<mapper namespace="mapper.StudentMapper">
<delete id="deleteByIds" parameterType="list">
delete from student where sid in
<foreach collection="list" item="x" open="(" close=")" separator=",">
#{x}
foreach>
delete>
<update id="update" parameterType="domain.Student">
update student
<set>
<if test="sname!=null">
sname=#{sname},
if>
<if test="birthday!=null">
birthday=#{birthday},
if>
<if test="sex!=null">
sex=#{sex}
if>
set>
where sid=#{sid}
update>
mapper>
案例1:表名和属性名不一致时 如何映射(除了起别名)例如属性名为sname,表列名为name;
解决方法:
resultMap是在需要复杂映射时,自己来定义更详细的映射方式;告诉Mybatis数据表列和对象属性的对应关系;
如果属性名和列名一致,则可以不用显示映射,但是如果像案例2那样存在嵌套映射则不能省略,可以添加``
<resultMap id="b" type="domain.Student">
<id column="id" property="id">id>
<result column="sname" property="name">result>
resultMap>
案例2:查询一级模块,每个一级模块又有自己对应的二级子模块;
注意新增了接口,要在配置文件中添加该接口
<mapper class="mapper.ModuleMapper"/>
表信息
+----+----------+-----+--------------------+
| id | name | pid | code |
+----+----------+-----+--------------------+
| 1 | 系统管理 | 0 | |
| 2 | 订单管理 | 0 | |
| 3 | 商品管理 | 0 | |
| 11 | 邮件设置 | 1 | /system/email |
| 12 | 短信设置 | 1 | /system/sms |
| 13 | 用户管理 | 1 | /system/user |
| 14 | 权限分配 | 1 | /system/role |
| 21 | 查询订单 | 2 | /order/search |
| 22 | 退单处理 | 2 | /order/refund |
| 23 | 统计分析 | 2 | /order/stat |
| 31 | 查询商品 | 3 | /product/search |
| 32 | 上下架 | 3 | /product/onoff |
| 33 | 统计分析 | 3 | /product/stat |
| 34 | 库存管理 | 3 | /product/inventory |
+----+----------+-----+--------------------+
方法一:
先查询一级模块,再根据一级module的id查新二级Module;N+1次查询
<mapper namespace="mapper.ModuleMapper">
<select id="selectModule" resultMap="b">
select id,name,pid,code from rbac_module where pid=0
select>
<resultMap id="b" type="domain.Module">
<id column="id" property="id">id>
<result column="name" property="name">result>
<result column="pid" property="pid">result>
<result column="code" property="code">result>
<collection property="children" select="findChildren" column="id">
collection>
resultMap>
<select id="findChildren" parameterType="int" resultType="domain.Module">
select id,name,pid,code from rbac_module where pid=#{pid}
select>
mapper>
方法二:
使用表连接,将所有模块都一次性查询出来,再根据列名和属性进行映射;
连接后的表
+----+----------+-----+------+-----+----------+------+--------------------+
| id | name | pid | code | bid | bname | bpid | bcode |
+----+----------+-----+------+-----+----------+------+--------------------+
| 1 | 系统管理 | 0 | | 11 | 邮件设置 | 1 | /system/email |
| 1 | 系统管理 | 0 | | 12 | 短信设置 | 1 | /system/sms |
| 1 | 系统管理 | 0 | | 13 | 用户管理 | 1 | /system/user |
| 1 | 系统管理 | 0 | | 14 | 权限分配 | 1 | /system/role |
| 2 | 订单管理 | 0 | | 21 | 查询订单 | 2 | /order/search |
| 2 | 订单管理 | 0 | | 22 | 退单处理 | 2 | /order/refund |
| 2 | 订单管理 | 0 | | 23 | 统计分析 | 2 | /order/stat |
| 3 | 商品管理 | 0 | | 31 | 查询商品 | 3 | /product/search |
| 3 | 商品管理 | 0 | | 32 | 上下架 | 3 | /product/onoff |
| 3 | 商品管理 | 0 | | 33 | 统计分析 | 3 | /product/stat |
| 3 | 商品管理 | 0 | | 34 | 库存管理 | 3 | /product/inventory |
+----+----------+-----+------+-----+----------+------+--------------------+
<select id="findAll" resultMap="c">
select a.*,b.id bid,b.name bname,b.pid bpid,b.code bcode
from rbac_module a inner join rbac_module b on a.id=b.pid
select>
<resultMap id="c" type="domain.Module">
<id column="id" property="id">id>
<result column="name" property="name">result>
<result column="pid" property="pid">result>
<result column="code" property="code">result>
<collection property="children" ofType="domain.Module">
<id column="bid" property="id">id>
<result column="bname" property="name">result>
<result column="bpid" property="pid">result>
<result column="bcode" property="code">result>
collection>
resultMap>
一级缓存
SqlSession自带缓存功能,缓存中没有才回去数据库中查询,否则直接返回缓存结果,称为一级缓存;缓存生命周期,SqlSession建立的时候开始缓存,直到SqlSession关闭,缓存清空;
每个SqlSession有自己独立的缓存并且互不干扰;
二级缓存
二级缓存是全局的,作用域整个SqlSession,需要额外设置,但可以允许多个SqlSession共享缓存数据;
好处:可以较大提升查询效率,避免了频繁访问数据库,在频繁增删改时不适合使用;
缺点:如果其他SqlSession修改了数据库的记录的时候缓存失效;
往二级缓存中存储时需要将对象序列化,取出来的时候需要反序列化;
标签,表示启动二级缓存;学习资料
深入学习Mybatis: