总结笔记,课程来自:https://www.kuangstudy.com/course/play/1321000734250762241
前言
-
Mybatis是什么?
-
我们为什么要学习Mybatis?
-
持久化是什么?
-
持久层是什么?
-
mybatis依赖
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version></dependency>
一、mybatis程序
1.思路:搭建环境->导入mybatis的jar包->编写代码->测试
2.mybatis程序
-
写工具类读取mybatis的配置文件获取sqlsession对象。
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 MybatisUtil { private static SqlSessionFactory sqlSessionFactory; static { try { // 使用mybatis第一步,获取sqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } // 有了SqlSessionFactory,我们就可以获得sqlsession的实例了 // sqlsession对象完全包含了面向数据库执行sql命令需要的所有方法 public static SqlSession getSqlSession() { return sqlSessionFactory.openSession(); }}
-
配置mybatis配置文件,连接数据库。
// 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://101.37.147.181:3306/z_mybatis?useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="xxx"/> </dataSource> </environment> </environments> <!--每一个mapper.xml都需要在mybatis核心配置文件中注册--> <mappers> <mapper resource="com/sywl/mapper/UserMapper.xml"></mapper> </mappers></configuration>
-
搭建环境,编写代码。(User,UserMapper接口,UserMapper.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.sywl.mapper.UserMapper"> <delete id="deleteUser"> delete from user where id = #{userId} </delete> <select id="selectUserList" resultType="com.sywl.entity.User"> select * from user </select> <select id="selectById" resultType="com.sywl.entity.User"> select * from user where id = #{id} </select> <insert id="insertUser" parameterType="com.sywl.entity.User"> insert into user(name,age) values (#{name},#{age}) </insert></mapper>
-
测试。
@Test public void test01(){ // 1.获取sqlsession对象 SqlSession sqlSession = MybatisUtil.getSqlSession(); // 2.方式一:获取UserMapper接口,去执行方法 UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.selectUserList(); for (User user : users) { System.out.println(user); } } @Test public void test05(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); List<User> userList = sqlSession.selectList("com.sywl.mapper.UserMapper.selectUserList"); for (User user : userList) { System.out.println(user); } }
3.可能出现的问题
- 配置文件没有注册。(mybatis配置文件中的没有配置)
- 绑定接口错误。(UserMapper.xml文件中的namespace配置不对)
- 方法名不对。(UserMapper.xml文件中id对应的方法名不对)
- 返回类型不对。(UserMapper.xml文件的resultType类型不对)
- 资源导出失败问题。(需要在pom.xml中的build中配置resources)
4.注意点
- resource绑定mapper,需要使用路径/
二、crud
1.万能Map
-
Map传递参数,直接在sq中取出Map的key。
Map map = new Map();map.set("name","zhangsan");map.set("userId",1);mapper.getUserById(map);// UserMapperUser getUserById(Map map);// UserMapper.xml<select id="getUserById" parameterType="map"> select * from user where username=#{name} or user_id=#{userId}</select>
-
对象传递参数,直接在sq中取对象的属性。
User user = new User();user.setUsername("zhangsan");user.setUserId(1);mapper.getUserById(user);// UserMapperUser getUserById(User user);// UserMapper.xml<select id="getUserById" parameterType="user"> select * from user where username=#{username} or user_id=#{userId}</select>
-
只有一个基本类型参数的情况下,可以直接在sq中取到,省略parameterType。
mapper.getUserById(id);// UserMapperUser getUserById(int id);// UserMapper.xml<select id="getUserById"> select * from user where username=#{username} or user_id=#{userId}</select>
-
多个参数传递时,用Map,或者@RequestParm注解。
2.模糊查询
-
java代码执行时传递通配符。
List<User> userList = mapper.getUserList("%李%");
-
在sql拼接时使用通配符。
select * from user where username like "%"#{username}"%"
三、配置优化
1.总配置
//db.properties 加上jdbc前缀避免出错(连接不到数据库)jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/mybatis?useEncoding=true&characterEncoding=utf-8jdbc.username=rootjdbc.password=xxx
// 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> <!--引入外部配置文件:可以增加属性--> <!--如果外部配置文件和增加的属性都用,优先使用外部配置--> <properties resource="db.properties"><!--db.properties在resources目录下可以直接写文件名--> <property name="username" value="root"/> <property name="password" value="xxx"/> </properties> <!--settings配置--> <settings> <!--全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。默认true--> <setting name="cacheEnabled" value="true"/> <!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。默认false--> <setting name="lazyLoadingEnabled" value="默认false"/> <!--允许JDBC支持自动生成主键,需要数据库驱动支持。默认false--> <setting name="useGeneratedKeys" value="false"/> <!--指定MyBatis所用日志的具体实现,未指定时将自动查找。默认未指定--> <setting name="logImpl" value="LOG4J"/> </settings> <!--别名设置:1给实体类起别名,2配置一个包名--> <typeAliases> <!--<typeAlias type="com.sywl.entity.User" alias="User"></typeAlias>--> <package name="com.sywl.entity"/> </typeAliases> <environments default="development"> <!--环境一--> <environment id="development"> <!--mybatis事务管理器:默认时JDBC,还有一个managed--> <transactionManager type="JDBC"/> <!--mybatis数据源:默认POOLED,还有UNPOOLED和JNDI--> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> <!--环境二--> <environment id="test"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <!--mappers映射器--> <mappers> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers></configuration>
2.相关配置属性
-
properties(属性)
-
settings(设置)
-
typeAliases(类型别名)
-
environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
不过要记住:尽管可以配置多个环境,但每个SqlSessionFactory实例只能选择一种环境。
- environment(环境变量)
-
mappers(映射器)
-
方式一:resource(推荐使用)
<mappers> <!--每一个Mapper.xml文件都需要在核心配置文件中注册--> <mapper resource="com/sywl/mapper/UserMapper.xml"/></mappers>
-
方式二:使用class文件绑定注册
<mappers> <!--每一个接口都需要在核心配置文件中注册--> <mapper class="com.sywl.mapper.UserMapper"/></mappers>// 注意://1接口和它的Mapper.xml配置文件必须同名。//2接口和它的Mapper.xml配置文件必须在同一个包下。
-
方式三:使用扫描包进行绑定注册。
<mappers> <package name="com.sywl.mapper"/></mappers>// 注意://1接口和它的Mapper.xml配置文件必须同名。//2接口和它的Mapper.xml配置文件必须在同一个包下。
-
四、生命周期和作用域
生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题
- SqlSessionFactoryBuider作用域。
- 一旦创建了SqlSessionFactory,就不再需要它了
- 因此SqlSessionFactoryBuilder实例的最佳作用域是方法作用域(局部方法变量)
- SqlSessionFactory作用域。
- SqlSessionFactory可以想象成连接池。
- SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
- 因此SqlSessionFactory的最佳作用域是应用作用域。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
- SqlSession作用域。
- 可以视为连接到连接池的一个请求。
- SqlSession的实例不是线程安全的,因此是不能被共享的。
- 所以它的最佳的作用域是请求或方法作用域。
- 用完之后关闭,否则资源会被占用。
- 为了确保每次都能执行关闭操作,你应该把这个关闭操作放到finally块中。
五、ResultMap
1.ResultMap结果集映射。
-
起别名
<mapper namespace="com.sywl.mapper.UserMapper"> <select id="getUserById" resultType="com.sywl.entity.User"> select id,username,pwd as password from User where id = #{id} </select></mapper>
-
ResultMap结果集映射(可以只映射字段名和属性名不同的)
<resultMap id="userMap" type="com.sywl.entity.User"> <id column="id" property="id"/><!--数据库字段id和实体类属性id名称一致可省略id的映射--> <result column="username" property="username"/><!--数据库字段username和实体类属性username名称一致可省略username的映射--> <result column="pwd" property="password"/><!--数据库字段pwd和实体类属性password名称不一致不可省略的映射--></resultMap><select id="getUserById" resultMap="userMap"> select id,username,pwd from User where id = #{id}</select>
2.ResultMap多对一映射
2.1按照查询嵌套处理
@Datapublic class Student { private int id; private String username; private Teacher teacher;}<mapper namespace="com.sywl.mapper.StudentMapper"> <!-- 思路:1.查询所有的学生 2.根据查询出来的学生表的tid字段,查找对应的老师 --> <resultMap id="studentTeacherMap" type="com.sywl.entity.Student"> <id property="id" column="id"></id> <result property="username" column="username"/> <!--对象:association,tid是学生表中关联的老师的id--> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap> <select id="getStudentList" resultMap="studentTeacherMap"> select * from student </select> <select id="getTeacher" resultType="Teacher"> select * from teacher where id = #{tid}<!--这个参数名推荐和上面column="tid"保持一致,但是不保持一致也不会出错--> </select></mapper>
2.1按照结果嵌套处理
@Datapublic class Student { private int id; private String username; private Teacher teacher;}<mapper namespace="com.sywl.mapper.StudentMapper"> <resultMap id="studentTeacherMap" type="com.sywl.entity.Student"> <id property="id" column="id"></id> <result property="username" column="username"/> <!--对象:association--> <association property="teacher" javaType="Teacher"> <id property="id" column="tid"></id> <result property="username" column="tname"></result> </association> </resultMap> <select id="getStudentList" resultMap="studentTeacherMap"> select s.id,s.username,t.id tid,t.username tname from student s,teacher t where s.tid=t.id </select></mapper>
3.ResultMap一对多映射
3.1按照查询嵌套处理
<mapper namespace="com.sywl.mapper.TeacherMapper"> <!-- 思路:1.查询指定的老师 2.根据查询出来的老师的id字段别名是ttid,查找老师下对应的所有学生 --> <resultMap id="teacherStudentMap" type="Teacher"> <id property="id" column="ttid"></id> <!--ttid是老师表的id--> <collection property="studentList" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="ttid"/> </resultMap> <!--按照查询嵌套处理--> <select id="getTeacherById" resultMap="teacherStudentMap"> select t.id ttid,t.* from teacher t where id=#{tid} </select> <select id="getStudentByTeacherId" resultType="Student"> select * from student where tid = #{ttid} </select></mapper>
3.2按照结果嵌套处理
@Datapublic class Teacher { private int id; private String username; private List<Student> studentList;}<mapper namespace="com.sywl.mapper.TeacherMapper"> <resultMap id="teacherStudentMap" type="Teacher"> <id property="id" column="tid"/> <result property="username" column="tname"/> <!-- 复杂的属性单独处理,集合:collection javaType=""指定实体类属性的类型; 集合中的泛型信息,使用ofType获取。 --> <collection property="studentList" ofType="Student"> <id property="id" column="tid"/> <result property="username" column="sname"/> <result property="tid" column="tid"/> </collection> </resultMap> <!--按结果嵌套查询--> <select id="getTeacherById" resultMap="teacherStudentMap"> select s.id sid,s.username sname,t.id tid,t.username tname from student s,teacher t where s.tid=t.id and t.id=#{tid} </select></mapper>
六、日志工厂
1.mybatis的日志配置
使用哪个日志实现在mybatis-config.xml设置中配置
- SLF4J
- LOG4J(掌握)
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING(掌握:标准日志输出)
- NO_LOGGING0
2.log4j
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件。
- 我们也可以控制每一条日志的输出格式。
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
- 最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
3.mybatis使用log4j步骤
-
导入log4j的jar包。
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version></dependency>
-
设置mybatis配置文件的日志实现类是LOG4J
<settings> <!--指定MyBatis所用日志的具体实现,未指定时将自动查找。默认未指定--> <setting name="logImpl" value="LOG4J"/></settings>
-
书写log4j.properties配置文件。
# 将等级为DEBUG的日志信息输出到console和file两个目的地log4j.rootLogger=DEBUG,console,file# 控制台输出相关配置log4j.appender.console=org.apache.log4j.ConsoleAppenderlog4j.appender.Threshold=DEBUGlog4j.appender.console.Target=System.outlog4j.appender.console.layout=org.apache.log4j.PatternLayoutlog4j.appender.console.layout.ConversionPattern=[%-5p][%d{yyyy-MM-dd HH\:mm\:ss,SSS}][%c] \:%m%n# 文件输出的相关配置log4j.appender.file=org.apache.log4j.RollingFileAppenderlog4j.appender.file.File=./logs/all.loglog4j.appender.file.MaxFileSize=10mblog4j.appender.file.Threshold=DEBUGlog4j.appender.file.layout=org.apache.log4j.PatternLayoutlog4j.appender.file.layout.ConversionPattern=[%-5p][%d{yyyy-MM-dd HH\:mm\:ss,SSS}][%c] \:%m%n# 日志输出级别log4j.logger.org.mybatis=DEBUGlog4j.logger.java.sql=DEBUGlog4j.logger.java.sql.Statement=DEBUGlog4j.logger.java.sql.ResultSet=DEBUGlog4j.logger.java.sql.PreparedStatement=DEBUG
七、分页和注解开发
1.limit分页
// i : 为查询结果的索引值(默认从0开始)// n : 为查询结果返回的数量select * from user limit i,n;// 传一个参数时,意思是从第一个数据开始查询n个数据。select * from user limit n;
2.RowBounds分页
// 代码成设置分页@Testpublic void test02(){ SqlSession sqlSession = SqlSessionUtil.getSqlSession(); RowBounds rowBounds = new RowBounds(1, 2); List<User> userList = sqlSession.selectList("com.sywl.mapper.UserMapper.selectList", rowBounds); }
3.mybatis的PageHelper分页插件。
4.注解开发
@Select("select * from user")List<User> selectUser();@Insert("")void insertUser(User user);@Update("")void updateUser(User user);@Delete("")void deleteUserById(int id);
八、mybatis执行流程
九、动态SQL
- 动态SQL是指根据不同的条件生成不同的SQL语句。
- 动态SQL本质还是SQL语句,只是我们可以在SQL层面,执行一个逻辑代码。
- 动态SQL就是在拼接SQL语句,我们只要保证SQL正确性,按照SQL格式去排列组合就可以了。
1.if
<select id="queryBlogIF" paremterType="map" resultType="blog"> select * from blog where 1=1 <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if></select>
2.choose,when,otherwise
<select id="queryBlogChoose" paremterType="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>
3.trim(where,set)
标签作用:
- where标签元素中有子元素返回任何内容的情况下才插入“where”子句。
- 如果子句的开头为”AND”或”OR”,where标签元素会将他们去除。
标签作用:
-
set标签元素会动态的在行首插入set关键字,并会删除额外的逗号。(这些逗号是在使用条件语句给列赋值时引入的)
<update id="updateAuthorIfNecessary">update Author <set> <if test="username != null">username=#{username},</if> <if test="password != null">password=#{password},</if> <if test="email != null">email=#{email},</if> <if test="bio != null">bio=#{bio}</if> </set>where id=#{id}</update>
4.foreach
<select id="selectPostIn" resultType="User"> select * from user u where id in <foreach item="id" index="index" collection="ids" open="(" separator="," close=")"> #{id} </foreach></select>
5.SQL片段
-
可以将一些功能的部分抽取出来,方便复用。
5.1使用SQL标签抽取公共的部分。
<sql id="if-title-author"> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if></sql>
5.2在需要的地方使用include标签引用即可。
<select id="queryBlogIF" paremterType="map" resultType="blog"> select * from blog <where> <include refid="if-title-anthor"></include> </where></select>
十、缓存
- 什么是缓存[Cache]?
- 存在内存中的临时数据。
- 将用户经常查询的数据放在缓存中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中査询,从而提高査询效率,解决了高并发系统的性能问题。
- 为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
- 什么样的数据能使用缓存?
- 经常査询并且不经常改变的数据。
1.mybatis缓存
- mybatis默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存)
- 二级缓存需要手动开启和配置,基于namespace级别的缓存。
- 为了提高扩展性,mybatis定义了缓存接口Cache。(我们可以通过实现Cache接口来自定义二级缓存)
2.一级缓存
2.1测试步骤
- 开启日志。
- 测试在一个SqlSession中查询两次相同的记录。
- 查看日志输出。(只查询了一次数据库SQL,第二次直接从缓存中数据)
2.2一级缓存失效的情况
- 查询不同的东西。
- 增删改操作,可能会改变原来的数据,所以必定会刷新缓存。
- 查询不同的mapper.xml
- 手动清理缓存。(sqlSession.clearCache();)
2.3一级缓存总结
- 一级缓存默认是开启的。
- 一级缓存只在一次SqlSession中有效,即拿到连接到连接关闭的这个区间。
3.二级缓存
3.1二级缓存使用步骤
-
开启全局缓存。
<settings> <!--默认是true,建议显式的在配置文件中声明--> <setting name="cacheEnabled" value="true"/></settings>
-
在要使用二级缓存的mapper.xml中开启。
<!--直接开启使用--><cache/><!--也可以自定义参数开启使用--><cacheeviction="FIFO"flushInterval="60000"size="512"readOnly="true"/>
LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
-
测试。
- 需要把实体类序列化(实体类实现Serializable接口),否则可能会报错。
3.2二级缓存总结
-
只要开启了二级缓存,在同一个mapper下有效。
-
所有的数据都会先放在一级缓存中。
-
只有当会话提交或关闭的时候,才会提交到二级缓存中。
-
可以在mapper.xml中的查询语句定义是否使用缓存
<select id="getUser"resultType="user" userCache="false"> select * from user</select>
4.缓存原理
5.自定义缓存-ehcache
Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
配置一个ehcache.xml配置
总结
- mybatis是一款优秀的持久层框架,可以把数据持久化;它支持定制化sql,存储过程,结果集映射等。
- 使用的人多,传统的jdbc代码太复杂了,mybatis框架简化了流程,自动化。
- 持久化是数据从瞬时状态转化到持久状态。内存的特性是断电即失,持久化指存到数据库中,io文件持久化等。
- 持久层是完成持久化工作的代码块。
面试题
- MySQL引擎
- InnoDB底层原理
- 索引
- 索引优化
- 读写分离
- 主从复制