MyBatis学习笔记
一款优秀的持久层(DAO)框架
1.第一个MyBatis程序 思路:
1.1 sqlSession工具类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.codelorin.utils;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;import java.io.InputStream;public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { String resource = "mybatis-config.xml" ; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getSqlSession () { return sqlSessionFactory.openSession(); } }
1.2 实体类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package com.codelorin.pojo;public class User { private int id; private String name; private String pwd; public User () { } public User (int id, String name, String pwd) { this .id = id; this .name = name; this .pwd = pwd; } public int getId () { return id; } public void setId (int id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public String getPwd () { return pwd; } public void setPwd (String pwd) { this .pwd = pwd; } @Override public String toString () { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}' ; } }
1.3 DAO层接口 1 2 3 4 5 6 7 8 9 10 package com.codelorin.dao;import com.codelorin.pojo.User;import java.util.List;public interface UserMapper { List<User> getUserList () ; }
1.4 mybatisMapper配置文件(接口实现类) 1 2 3 4 5 6 7 8 9 10 11 12 <?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.codelorin.dao.UserMapper" > <select id ="getUserList" resultType ="com.codelorin.pojo.User" > select * from test.user </select > </mapper >
1.5 mybatis数据库配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?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/test?useUnicode=true& characterEncoding=utf8& useSSL=true" /> <property name ="username" value ="root" /> <property name ="password" value ="123456" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="com/codelorin/dao/UserMapper.xml" /> </mappers > </configuration >
1.6 junit测试
注意
org.apache.ibatis.binding.BindingException: Type interface com.codelorin.dao.UserDao is not known to the MapperRegistry.
资源导出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <build > <resources > <resource > <directory > src/main/resources</directory > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <filtering > true</filtering > </resource > <resource > <directory > src/main/java</directory > <includes > <include > **/*.properties</include > <include > **/*.xml</include > </includes > <filtering > true</filtering > </resource > </resources > </build >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.codelorin.dao;import com.codelorin.pojo.User;import com.codelorin.utils.MybatisUtils;import org.apache.ibatis.session.SqlSession;import org.junit.Test;import java.util.List;public class UserDaoTest { @Test public void test () { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> userList = userMapper.getUserList(); List<User> userList1 = sqlSession.selectList("com.codelorin.dao.UserMapper.getUserList" ); for (User user : userList1) { System.out.println(user); } sqlSession.close(); } }
2.CRUD实现 1.namespace 包名要和dao/mapper接口包名一致
2.select 选择,查询
id 就是对应namespace中的方法名
resultType: sql语句执行的返回值
parameteType: 参数类型
2.1 mapper 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <?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.codelorin.dao.UserMapper" > <select id ="getUserList" resultType ="com.codelorin.pojo.User" > select * from user </select > <select id ="getUserById" parameterType ="int" resultType ="com.codelorin.pojo.User" > select * from user where id = #{id} </select > <insert id ="addUser" parameterType ="com.codelorin.pojo.User" > insert into user (id, name, pwd) values (#{id}, #{name}, #{pwd}) </insert > <update id ="updateUser" parameterType ="com.codelorin.pojo.User" > update user set name = #{name}, pwd=#{pwd} where id = #{id} </update > <delete id ="delUser" parameterType ="int" > delete from user where id = #{id} </delete > </mapper >
2.2 接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.codelorin.dao;import com.codelorin.pojo.User;import java.util.List;public interface UserMapper { List<User> getUserList () ; User getUserById (int id) ; int addUser (User user) ; int updateUser (User user) ; int delUser (int id) ; }
2.3 测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 package com.codelorin.dao;import com.codelorin.pojo.User;import com.codelorin.utils.MybatisUtils;import org.apache.ibatis.session.SqlSession;import org.junit.Test;import java.util.List;public class UserMapperTest { @Test public void test () { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> userList = userMapper.getUserList(); for (User user : userList) { System.out.println(user); } sqlSession.close(); } @Test public void getUserById () { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User use = mapper.getUserById(1 ); System.out.println(use); sqlSession.close(); } @Test public void addUser () { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int res = mapper.addUser(new User(5 , "isxxl11" , "codexxl" )); System.out.println(res); sqlSession.commit(); sqlSession.close(); } @Test public void updateUser () { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.updateUser(new User(5 , "hahah" , "xxl" )); sqlSession.commit(); sqlSession.close(); } @Test public void delUser () { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.delUser(5 ); sqlSession.commit(); sqlSession.close(); } }
3.万能map
当实体类或字段过多可以使用 id为主键自动增加使用(个人理解)
1 int addUser2 (Map<String, Object> map) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void addUser2 () { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); HashMap<String, Object> map = new HashMap<String, Object>(); map.put("userId" , 10 ); map.put("username" , "xxl" ); map.put("password" , "hahah" ); mapper.addUser2(map); sqlSession.commit(); sqlSession.close(); }
1 2 3 4 <insert id ="addUser2" parameterType ="map" > insert into user (id, name, pwd) values (#{userId}, #{username}, #{password}) </insert >
4.模糊查询
在参数中拼接%
sql语句中拼接
1 2 3 4 5 <select id ="getUserLike" resultType ="com.codelorin.pojo.User" > select * from user where name like "%"#{value}"%" </select >
5.配置解析 1.核心配置文件
mybatis-config.xml
5.1.1 environment 1 2 3 4 5 6 7 8 9 <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?useUnicode=true& characterEncoding=utf8& useSSL=true" /> <property name ="username" value ="root" /> <property name ="password" value ="123456" /> </dataSource >
id:名称
type:事务管理器(jdbc | managed)
pooled:数据源的配置(UNPOOLED|POOLED|JNDI)
5.1.2 properties(属性)
可以通过它读取一些配置
1 <properties resource ="db.properties" />
5.1.3 类型别名
为java类型设置一个短的名字
存在的意义仅在于用来减少类完全限定名的冗余
1 2 3 4 5 <typeAliases > <typeAlias type ="com.codelorin.pojo.User" alias ="User" /> <package name ="com.codelorin.pojo" /> </typeAliases >
typeAlias:一一对应
package:制定一个包下,它会自动搜索,但是使用的时候使用名字的小写
注解:@Alias()
5.1.4 设置(settings)
日志
其他配置
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
mybatis-core
mybatis-plus
5.1.5 映射器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ```xml <mappers > <mapper resource ="com/codelorin/dao/UserMapper.xml" /> </mappers > ``` ```xml <mappers > <mapper class ="org.mybatis.builder.AuthorMapper" /> <mapper class ="org.mybatis.builder.BlogMapper" /> <mapper class ="org.mybatis.builder.PostMapper" /> </mappers > ``` ```xml <mappers > <package name ="org.mybatis.builder" /> </mappers > ```
6.生命周期和作用域
生命周期和作用域十分重要,错误使用会对严重的并发问题
6.1 sqlSessinFactoryBuilder
一点创建了sqlSessionFactory,就不再需要它了
局部变量
6.2 sqlSessionFactory
数据库连接池
一旦创建就在程序运行期间一直存在,没有任何理由丢弃它或者重新创建另外一个实例
6.3 sqlSession
连接到连接池的一个请求
需要开启和关闭,用完之后立刻关闭
7.解决属性名和字段名不一致的问题
数据库sql使用as
resultMap(结果集映射)
1 2 3 4 5 6 <resultMap id ="UserMap" type ="User" > # column数据库字段,property实体类字段 <result column ="id" property ="id" /> <result column ="name" property ="name" /> <result column ="pwd" property ="password" /> </resultMap >
8.日志 8.1日志工厂
1 2 3 4 5 6 <settings > <setting name ="logImpl" value ="STDOUT_LOGGING" /> </settings > <settings > <setting name ="logImpl" value ="LOG4J" /> </settings >
导入包
1 2 3 4 5 <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.12</version > </dependency >
配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 log4j.rootLogger =DEBUG,console,file log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold =DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern =[%c]-%m%n log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File =./log/sys.log log4j.appender.file.MaxFileSize =10mb log4j.appender.file.Threshold =DEBUG log4j.appender.file.layout =org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern =[%p][%d{yyyy-MM-dd hh:mm:ss}][%c]%m%n log4j.logger.org.mybatis =DEBUG log4j.logger.java.sql =DEBUG log4j.logger.java.sql.Statement =DEBUG log4j.logger.java.sql.ResultSet =DEBUG log4j.logger.java.sql.PreparedStatement =DEBUG
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.codelorin.dao;import com.codelorin.pojo.User;import com.codelorin.utils.MybatisUtils;import org.apache.ibatis.session.SqlSession;import org.apache.log4j.Logger;import org.junit.Test;import java.util.List;public class UserMapperTest { static Logger logger = Logger.getLogger(UserMapperTest.class); @Test public void log4jTest () { logger.info("info:进入了log4jTest方法" ); logger.debug("debug:进入了log4jTest方法" ); logger.error("error:进入了log4jTest方法" ); } }
9.分页limit 1 2 3 select * from user limit i,ni : 为查询结果的索引值(默认从0 开始); n : 为查询结果返回的数量
1 2 List<User> getUserByLimit (Map<String, Integer> map) ;
1 2 3 <select id ="getUserByLimit" parameterType ="map" resultType ="user" > select * from user limit #{startIndex},#{pageSize} </select >
测试1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void limitTest () { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); HashMap<String, Integer> map = new HashMap<>(); map.put("startIndex" , 0 ); map.put("pageSize" , 2 ); List<User> userList = mapper.getUserByLimit(map); for (User user : userList) { System.out.println(user); } sqlSession.close(); }
9.1 RowBounds分页
java层面实现的分页,了解就行
9.2 分页插件
PageHelper
10.使用注解开发
使用反射获取注解简化开发
11.mybatis执行流程剖析
resources获取全局配置文件
实例化SQLSessionFactoryBuilder构造器
解析配置文件流XMLConfigBuilder
Configuration所有的配置信息
SqlSessionFactory实例化
transactional事务管理
创建executor执行器
创建sqlSession
实现curd,错误就回滚
提交事务
关闭
12.Lombok
安装Lombok插件
maven导入jar包
实体类上加注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Getter and @Setter @FieldNameConstants @ToString @EqualsAndHashCode @AllArgsConstructor , @RequiredArgsConstructor and @NoArgsConstructor @Log , @Log4j , @Log4j2 , @Slf4j , @XSlf4j , @CommonsLog , @JBossLog , @Flogger , @CustomLog @Data @Builder @SuperBuilder @Singular @Delegate @Value @Accessors @Wither @With @SneakyThrows @val @UtilityClass @Data 无参 get set tostring hashcode equals@AllArgsConstructor and @NoArgsConstructor 有参无参
13.多对一的处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?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.codelorin.dao.StudentMapper" > <select id ="getStudent" resultType ="StudentTeacher" > select * from student </select > <resultMap id ="StudentTeacher" type ="Student" > <result property ="id" column ="id" /> <result property ="name" column ="name" /> <association property ="teacher" column ="tid" javaType ="Teacher" select ="getTeacher" /> </resultMap > <select id ="getTeacher" resultType ="Teacher" > select * from teacher where id = #{id} </select > </mapper >
1 2 3 4 5 6 7 8 9 10 11 12 13 <select id ="getStudent2" resultMap ="StudentTeacher2" > select s.id sid, s.name sname, t.name tname from student s, teacher t where s.tid = t.id </select > <resultMap id ="StudentTeacher2" type ="Student" > <result property ="id" column ="sid" /> <result property ="name" column ="sname" /> <association property ="teacher" javaType ="Teacher" > <result property ="name" column ="tname" /> </association > </resultMap >
14.一对多的处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <select id ="getTeacher2" resultMap ="TeacherStudent2" > select * from teacher where id = #{id} </select > <resultMap id ="TeacherStudent2" type ="Teacher" > <collection property ="students" column ="id" ofType ="Student" select ="getStudentByTeacherId" /> </resultMap > <select id ="getStudentByTeacherId" resultType ="Student" > select * from student where tid = #{id} </select >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?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.codelorin.dao.TeacherMapper" > <select id ="getTeacher" resultMap ="TeacherStudent" > select s.id sid, s.name sname, t.name tname, t.id tid from student s, teacher t where s.tid = t.id and t.id = #{id} </select > <resultMap id ="TeacherStudent" type ="Teacher" > <result property ="id" column ="tid" /> <result property ="name" column ="tname" /> <collection property ="students" ofType ="Student" > <result property ="id" column ="sid" /> <result property ="name" column ="sname" /> <result property ="tid" column ="tid" /> </collection > </resultMap > </mapper >
总结
15.动态sql
根据不同的条件生成不同的sql语句
1 2 3 4 5 6 7 8 9 10 11 12 13 <select id ="queryBlogIf" parameterType ="map" resultType ="blog" > select * from blog <where > <if test ="title != null" > title = #{title} </if > <if test ="author != null" > and author = #{author} </if > </where > </select >
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void test () { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap hashMap = new HashMap(); hashMap.put("title" , "数据库" ); List<Blog> blogs = mapper.queryBlogIf(hashMap); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <select id ="queryBlogChoose" parameterType ="map" resultType ="blog" > select * from blog <where > <choose > <when test ="title != null" > title = #{title} </when > <when test ="author != null" > and author = #{author} </when > <otherwise > and views = #{views} </otherwise > </choose > </where > </select >
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)
1 2 3 4 5 6 7 8 9 10 11 12 <update id ="updateBlog" parameterType ="map" > update blog <set > <if test ="title != null" > title = #{title}, </if > <if test ="author != null" > author = #{author} </if > </set > where id = #{id} </update >
1 2 3 4 5 6 <trim prefix ="WHERE" prefixOverrides ="AND |OR " > ... </trim > <trim prefix ="SET" suffixOverrides ="," > ... </trim >
1 2 3 4 5 6 7 8 9 10 <sql id ="if-tile-author" > <if test ="title != null" > title = #{title} </if > <if test ="author != null" > and author = #{author} </if > </sql > <include refid ="if-tile-author" > </include >
1 2 3 4 5 6 7 8 <select id ="queryBlogForEach" parameterType ="map" resultType ="blog" > select * from blog where id in <foreach collection ="ids" item ="id" open ="(" separator ="," close =")" > #{id} </foreach > </select >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void test () { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap hashMap = new HashMap(); ArrayList<Integer> ids = new ArrayList<>(); ids.add(1 ); ids.add(2 ); ids.add(4 ); hashMap.put("ids" ,ids); List<Blog> blogs = mapper.queryBlogForEach(hashMap); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close(); }
16.缓存
读写分离,主从复制
映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
缓存不会定时进行刷新(也就是说,没有刷新间隔)。
缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
1 2 #清理缓存 sqlSession.clearCache();
sqlSession级别的缓存,只在一次会话中使用.
1.开启全局缓存
1 <setting name ="cacheEnabled" value ="true" />
2.
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
这些属性可以通过 cache 元素的属性来修改。比如:
1 2 3 4 5 <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
可用的清除策略有:
LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 LRU。
flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
3.测试
我们需要将实体类序列化
1 public class Blog implements Serializable
小结
只要开启了二级缓存,在同一个mapper下有效
所有的数据都放在一级缓存中
当会话提交或者关闭时,才会提交到二级缓存中
ehcache
1 2 3 4 5 6 7 <dependency > <groupId > org.mybatis.caches</groupId > <artifactId > mybatis-ehcache</artifactId > <version > 1.2.1</version > </dependency >
1 <cache type ="org.mybatis.caches.ehcache.EhcacheCache" />