【狂神说】Mybatis最新完整教程IDEA版参考链接:https://www.bilibili.com/video/BV1NE411Q7Nx 【狂神说】Java学习完整路线https://www.bilibili.com/read/cv5702420
如何获得Mybatis
maven仓库: <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> 中文文档:https://mybatis.org/mybatis-3/zh/index.htmlGithub:https://github.com/mybatis/mybatis-3数据持久化
持久化就是将程序的数据在持久状态和瞬时状态转化的过程内存:断电即失数据库(Jdbc),io文件持久化生活方面例子:冷藏,罐头。为什么需要持久化?
不想丢掉一些对象内存太贵Dao层,Service层,Controller层…
完成持久化工作的代码块层界限十分明显最重要的一点:使用的人多! Spring-SpringMVC-SpringBoot
思路:搭建环境–>导入Mybatis–>编写代码–>测试
搭建数据库 新建项目
新建一个普通maven项目删除src目录导入maven依赖 <!--import dependencies--> <dependencies> <!--mysql driver--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!--mybatis--> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>自己在此用了mybatis3.5.2版本在之后导包会失败,所以换成3.5.6版本。
此处设useSSL=false。
编写mybatis工具类 //SqlSessionFactory -->SqlSession public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { //使用Mybaties第一步:获取sqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (Exception e) { e.printStackTrace(); } } //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。 // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。 public static SqlSession getSqlSession(){ // SqlSession sqlSession = sqlSessionFactory.openSession(); // return sqlSession; return sqlSessionFactory.openSession(); } }注意点:
org.apache.ibatis.binding.BindingException: Type interface com.kuang.dao.UserDao is not known to the MapperRegistry.MapperRegistry是什么?
核心配置文件中注册mappers
junit测试 @Test public void test(){ //第一步:获得SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); //方式一:getMapper UserDao userDao = sqlSession.getMapper(UserDao.class); List<User> userList = userDao.getUserList(); for (User user : userList) { System.out.println(user); } //关闭SqlSession sqlSession.close(); }可能遇到的问题:
配置文件没有注册;绑定接口错误;方法名不对;返回类型不对;Maven导出资源问题。namespace中的包名要和Dao/Mapper接口的包名一致!
选择,查询语句;
id:就是对应的namespace中的方法名;resultType:Sql语句执行的返回值!parameterType:参数类型!1.编写接口
//根据id查询用户 User getUserById(int id);2.编写对应的mapper中的sql语句
<select id="getUserById" parameterType="int" resultType="com.kuang.pojo.User"> select * from mybatis.user where id = #{id} </select>3.测试
@Test public void getUserById(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); //关闭SqlSession sqlSession.close(); }注意点:
增删改需要提交事务!即可成功测试。
标签不要匹配错!resource绑定mapper,需要使用路径!程序配置文件必须符合规范!NullPointerException,没有注册到资源!maven资源没有导出问题!假设,我们的实体类,或者数据库中的表,字段或者参数过多,我们应当考虑使用Map!
//万能的Map int addUser2(Map<String,Object> map); <!--对象中的属性,可以直接取出来 传递map的key--> <insert id="addUser2" parameterType="map"> insert into mybatis.user (id,pwd) values (#{userid},#{password}) </insert> @Test public void addUser2(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map<String,Object> map = new HashMap<String, Object>(); map.put("userid",4); map.put("password","123321"); mapper.addUser2(map); sqlSession.commit(); sqlSession.close(); }Map传递参数,直接在sql中取出key即可!【parameterType=“map”】 对象传递参数,直接在sql中取对象的属性即可!【parameterType=“Object”】 只有一个基本类型参数的情况下,可以直接在sql中取到! 多个参数用Map,或者注解!
模糊查询怎么写?
java代码执行的时候,传递通配符% % List<User> userList = mapper.getUserLike("%李%"); 在sql拼接中使用通配符! select * from mybatis.user where name like "%"#{value}"%"Mybatis可以配置成适应多种环境 不过要记住:尽管可以配置多个环境,但每个SqlSessionFactory实例只能选择一种环境。 学会使用配置多套运行环境! Mybatis默认的事务管理器就是JDBC,连接池:POOLED
我们可以通过properties属性来实现引用配置文件 这些属性都是可外部配置且可动态替换的,既可以在典型的Java属性文件中配置,亦可通过properties元素的子元素来传递。【db.properties】 编写一个配置文件 db.properties
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8 username=root password=123456在核心配置文件中映入
<!--引入外部配置文件--> <properties resource="db.properties"> <property name="username" value="root"/> <property name="pwd" value="123123"/> </properties> 可以直接引入外部文件可以在其中增加一些属性配置如果两个文件有同一个字段,优先使用外部配置文件的!也可以指定一个包名,MyBatis会在包名下面搜索需要的JavaBean,比如: 扫描实体类的包,它的默认别名就为这个类的类名,首字母小写!
<!--可以给实体类起别名--> <typeAliases> <package name="com.kuang.pojo"/> </typeAliases>在实体类比较少的时候,使用第一种方式。 如果实体类十分多,建议使用第二种。 第一种可以DIY别名,第二种则不行,如果非要改,需要在实体上增加注解
@Alias("user") //实体类 public class User {}这是MyBatis中极为重要的调整设置,它们会改变MyBatis的运行时行为。
MapperRegistry:注册绑定我们的Mapper文件; 方式一:【推荐使用】
<!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册!--> <mappers> <mapper resource="com/kuang/dao/UserMapper.xml"/> </mappers>方式二:使用class文件绑定注册
<!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册!--> <mappers> <mapper class="com.kuang.dao.UserMapper"/> </mappers>注意点:
接口和它的Mapper配置文件必须同名!接口和它的Mapper配置文件必须在同一个包下!方式三:使用扫描包进行注入绑定
<!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册!--> <mappers> <package name="com.kuang.dao"/> </mappers>注意点:
接口和它的Mapper配置文件必须同名!接口和它的Mapper配置文件必须在同一个包下!练习:
将数据库配置文件外部引入实体类别名保证UserMapper接口和UserMapper.xml改为一致!并且放在同一个包下!生命周期和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder:
一旦创建了 SqlSessionFactory,就不再需要它了。局部变量SqlSessionFactory:
说白就是可以想象为:数据库连接池。SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。SqlSessionFactory 的最佳作用域是应用作用域。最简单的就是使用单例模式或者静态单例模式。SqlSession:
连接到连接池的一个请求!SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。用完后需要赶紧关闭,否则资源被占用! 这里的每一个Mapper,就代表一个具体的业务!数据库中的字段 新建一个项目,拷贝之前的,测试实体类字段不一致的情况
public class User { private int id; private String name; private String password; }测试出现问题
// select * from mybatis.user where id = #{id} // 类型处理器 // select id,name,pwd from mybatis.user where id = #{id}解决方法:
起别名 <select id="getUserById" parameterType="int" resultType="user"> select id,name,pwd as password from mybatis.user where id = #{id} </select> resultMap解决结果集映射
id name pwd id name password <!-- 结果集映射 --> <resultMap id="UserMap" type="User"> <!--column数据库中的字段,property实体类中的属性--> <result column="id" property="id" /> <result column="name" property="name" /> <result column="pwd" property="password" /> </resultMap> <select id="getUserById" parameterType="int" resultMap="UserMap"> select * from mybatis.user where id = #{id} </select> resultMap 元素是 MyBatis 中最重要最强大的元素。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。ResultMap 的优秀之处——你完全可以不用显式地配置它们。如果这个世界总是这么简单就好了。如果一个数据库操作出现了异常,我们需要排错。日志就是最好的助手! 曾经:sout、debug 现在:日志工厂!
SLF4JLOG4J 【掌握】LOG4J2JDK_LOGGINGCOMMONS_LOGGINGSTDOUT_LOGGING【掌握】NO_LOGGING在Mybatis中具体使用哪一个日志实现,在设置中设定!
STDOUT_LOGGING标准日志输出 在mybatis-config.xml核心配置文件中,配置我们的日志!
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>什么是Log4j?
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。1.先在pom.xml文件中导入log4j的依赖包
<dependencies> <!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>2.在resources文件夹下建立log4j.properties文件进行配置
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 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/kuang.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{yy-MM-dd}][%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=DEBUG3.在mybatis-config.xml核心配置文件中,配置log4j为日志的实现!
<settings> <setting name="logImpl" value="LOG4J"/> </settings>4.Log4j的使用,直接测试运行 简单使用
在要使用Log4j的测试类中,导入包import org.apache.log4j.Logger;日志对象,参数为当前类的class static Logger logger = Logger.getLogger(UserDaoTest.class); 日志级别 logger.info("info:进入了testLog4j"); logger.debug("DEBUG:进入了testLog4j"); logger.error("erro:进入了testLog4j"); 之后可在log文件夹中查看日志文件信息思考:为什么要分页!
减少数据的处理量使用Mybatis实现分页,核心SQL
接口 //分页 List<User> getUserByLimit(Map<String,Integer> map); Mapper.xml <!-- 分页--> <select id="getUserByLimit" parameterType="map" resultMap="UserMap"> select * from mybatis.user limit #{startIndex},#{pageSize} </select> 测试 @Test public void getUserByLimit(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); HashMap<String, Integer> map = new HashMap<String, Integer>(); map.put("startIndex",0); map.put("pageSize",2); List<User> userList = mapper.getUserByLimit(map); for (User user : userList) { System.out.println(user); } sqlSession.close(); }不再使用SQL实现分页
接口 //分页2 List<User> getUserByRowBounds(); Mapper.xml <!-- 分页2--> <select id="getUserByRowBounds" resultMap="UserMap"> select * from mybatis.user </select> 测试 @Test public void getUserByRowBounds(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); //RowBounds实现 RowBounds rowBounds = new RowBounds(0, 2); //通过java代码层面实现分页 List<User> userList = sqlSession.selectList("com.kuang.dao.UserMapper.getUserByRowBounds",null,rowBounds); for (User user : userList) { System.out.println(user); } sqlSession.close(); }了解即可,使用时,需要知道是什么东西!
本质:反射机制实现 底层:动态代理!
Mybatis详细的执行流程!(之后应用的多了详细再走一遍)
【注意:我们必须要将接口注册绑定到我们的核心配置文件中!】
关于@Param()注解
基本类型的参数或者String类型,需要加上引用类型不需要加如果只有一个基本类型的话,可以忽略,但是建议都加上!我们在SQL中引用的就是我们这里的@Param("")中设定的属性名! #{}和${}区别使用步骤:
在IDEA中安装Lombok插件!在项目pom.xml文件中导入Lombok的jar包 <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency> 在实体类上加注解即可! @Data @AllArgsConstructor @NoArgsConstructor @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说明:
@Data:无参构造、get、set、toString、hashCode、equals @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode @ToString @Getter and @Setter多对一:
多个学生,对应一个老师对于学生而言,关联–多个学生,关联一个老师【多对一】对于老师而言,集合–一个老师,有很多个学生【一对多】SQL语句:
CREATE TABLE `teacher` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, PRIMARY KEY (`id`) )ENGINE = INNODB DEFAULT CHARSET=utf8 INSERT INTO teacher(`id`,`name`) VALUES (1,'秦老师'); CREATE TABLE `student` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, `tid` INT(10) DEFAULT NULL, PRIMARY KEY (`id`), KEY `fktid`(`tid`), CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`) )ENGINE = INNODB DEFAULT CHARSET=utf8 INSERT INTO `student`(`id`,`name`,`tid`) VALUES ('1','小明','1'); INSERT INTO `student`(`id`,`name`,`tid`) VALUES ('2','小红','1'); INSERT INTO `student`(`id`,`name`,`tid`) VALUES ('3','小张','1'); INSERT INTO `student`(`id`,`name`,`tid`) VALUES ('4','小李','1'); INSERT INTO `student`(`id`,`name`,`tid`) VALUES ('5','小王','1');回顾Mysql多对一查询方式:
子查询联表查询比如:一个老师拥有多个学生! 对于老师而言,就是一对多的关系!
注意点:
保证SQL的可读性,尽量保证通俗易懂注意一对多和多对一中,属性名和字段的问题!如果问题不好排查错误,可以使用日志,建议使用Log4j面试高频
Mysql引擎InnoDB底层原理索引索引优化什么是动态SQL:动态SQL就是 指根据不同的条件生成不同的SQL语句
利用动态SQL这一特性可以彻底摆脱这种痛苦。
在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。 if choose (when, otherwise) trim (where, set) foreach创建一个基础工程
导包编写配置文件编写实体类 @Data public class Blog { private String id; private String title; private String author; private Date createTime; //属性名和字段名不一致 private int views; } 编写实体类对应Mapper接口和Mapper.XML文件所谓的动态SQL,本质还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!
提示你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
<!--select * from blog where 1=1 and (id=1 or id=2 or id=3) 我们现在传递一个万能的map,这map中可以存在一个集合! --> <select id="queryBlogForeach" parameterType="map" resultType="Blog"> select * from mybatis.blog <where> <foreach collection="ids" item="id" open="and (" close=")" separator="or"> id = #{id} </foreach> </where> </select>动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了。 建议:
先在Mysql中写出完整的SQL,再对应的去修改成我们的动态SQL实现通用即可!有的时候,我们可以能会将一些功能的部分抽取出来,方便复用!
使用SQL标签抽取公共的部分 <sql id="if-title-author"> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </sql> 在需要使用的地方使用Include标签引用即可 <select id="queryBlogIF" parameterType="map" resultType="Blog"> select * from mybatis.blog <where> <include refid="if-title-author"></include> </where> </select>注意事项:
最好基于单表来定义SQL片段!不要存在where标签10月28和29日因为身体不舒服耽搁了两天
测试步骤:
开启日志!测试在一个Session中查询两次相同记录查看日志输出缓存失效的情况:
查询不同的东西;
增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
查询不同的Mapper.xml
手动清理缓存!
小结:一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段! 一级缓存相当于一个Map。
步骤:
在mybatis-config.xml开启全局缓存 <!--显示的开启全局缓存--> <setting name="cacheEnabled" value="true"/> 在要使用二级缓存的Mapper中开启 <!--在当前Mapper.xml中使用二级缓存--> <cache/>也可以自定义参数
<!--在当前Mapper.xml中使用二级缓存--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> 测试 问题:如果没有自定义参数,则会报错,我们需要将实体类序列化! Cause: java.io.NotSerializableException: com.kuang.pojo.User小结:
只要开启了二级缓存,在同一个Mapper下就有效;所有的数据都会先放在一级缓存中;只有当会话提交或者关闭的时候,才会提交到二级缓存中!Ehcache是一种广泛使用的开源Java分布式缓存,主要面向通用缓存。
要在程序中使用ehcache,先要导包!
在mapper中指定使用我们的ehcache缓存实现!
目前:Redis数据库来做缓存!K-V
一方面,跟着视频学习,并记录下学习笔记,方便以后复习回顾。另一方面,标注时间只为了解自己的学习进度,宁保质量不比速度,学习路程还很遥远,继续加油坚持!!!