花了四天的时间,终于把MyBatis的视频教程全部看完了,为了以后不要那么快的遗忘掉,还是来写一下笔记比较好。
下面的内容都是基于遇见狂神说的MyBatis视频教程而来,在此感谢能提供这么优秀的教程。
# 简介
- MyBatis的作用用我自己的话说,就是不用自己手动搞JDBC了,省事,行!
- MyBatis作用于持久层——DAO层 【DAO (Data Access Object) 数据访问对象】,通俗点说就是用来操作数据库的层
- 选择MyBatis的最大原因还是用的人多,为后面的Spring打基础_(:з)∠)_
# 第一个MyBatis程序
思路流程:搭建环境–>导入MyBatis—>编写代码—>测试
# 具体代码
-
创建Maven项目
-
引入依赖包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
<!-- 导入依赖 --> <dependencies> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.25</version> <scope>runtime</scope> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <!-- junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
-
在
resources
目录下创建mybatis-config.xml
,作为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 25
<?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.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!--每一个mapper都需要注册--> <mappers> <mapper resource="UserMapper.xml"/> </mappers> </configuration>
-
编写MyBatis工具类,在
utils
的包下面创建MybatisUtils
类,填入以下代码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
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; //SqlSessionFactory /** * @author luoboQAQ */ public class MybatisUtils { 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(); } }
-
在
pojo
包下创建实体类1 2 3 4 5 6 7 8 9
public class User { private int id; //id private String name; //姓名 private String pwd; //密码 //构造,有参,无参 //set/get //toString() }
-
在
dao
包下创建接口类,我们需要的功能就填入到这里面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
import top.lbqaq.pojo.User; import java.util.List; import java.util.Map; public interface UserMapper { //模糊查询 List<User> getUserLike(String value); //查询全部用户 List<User> getUserList(); //根据ID查询用户 User getUserById(int id); //插入用户 int addUser(User user); int addUser2(Map<String,Object> map); //修改用户 int updateUser(User user); //删除用户 int deleteUser(int id); }
-
编写
Mapper.xml
配置文件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
<?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绑定一个对应的Dao/Mapper接口--> <mapper namespace="top.lbqaq.dao.UserMapper"> <!--id就是namespace中对应的方法名,resultType:sql语句的返回值--> <select id="getUserList" resultType="top.lbqaq.pojo.User"> select * from user </select> <select id="getUserLike" resultType="top.lbqaq.pojo.User"> select * from user where name like #{value} </select> <!--parameterType:传入参数类型--> <select id="getUserById" parameterType="int" resultType="top.lbqaq.pojo.User"> select * from user where id=#{id} </select> <insert id="addUser" parameterType="top.lbqaq.pojo.User"> insert into user (id,name,pwd) values (#{id},#{name},#{pwd}) </insert> <!--传递map的key--> <insert id="addUser2" parameterType="map"> insert into user (id,name,pwd) values (#{userid},#{userName},#{passWord}) </insert> <update id="updateUser" parameterType="top.lbqaq.pojo.User"> update user set name =#{name},pwd=#{pwd} where id=#{id} </update> <delete id="deleteUser" parameterType="int"> delete from user where id=#{id} </delete> </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 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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
import org.apache.ibatis.session.SqlSession; import org.junit.Test; import top.lbqaq.pojo.User; import top.lbqaq.utils.MybatisUtils; import java.util.HashMap; import java.util.List; public class UserMapperTest { @Test public void test() { //获得sqlSession对象 try (SqlSession sqlSession = MybatisUtils.getSqlSession()) { //执行SQL UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.getUserList(); for (User user : userList) { System.out.println(user); } } } @Test public void getUserLike() { //获得sqlSession对象 try (SqlSession sqlSession = MybatisUtils.getSqlSession()) { //执行SQL UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.getUserLike("%李%"); for (User user : userList) { System.out.println(user); } } } @Test public void getUserById() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); sqlSession.close(); } @Test public void addUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int res = mapper.addUser(new User(4, "小明", "123")); if(res>0){ System.out.println("提交成功"); //提交事务 sqlSession.commit(); } sqlSession.close(); } @Test public void addUser2(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); HashMap<String,Object> map= new HashMap<>(); map.put("userid",5); map.put("userName","小绿"); map.put("passWord","1234"); int res = mapper.addUser2(map); if(res>0){ System.out.println("提交成功"); //提交事务 sqlSession.commit(); } sqlSession.close(); } @Test public void updateUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int res = mapper.updateUser(new User(4, "小明", "123456")); if(res>0){ System.out.println("提交成功"); //提交事务 sqlSession.commit(); } sqlSession.close(); } @Test public void deleteUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int res = mapper.deleteUser(4); if(res>0){ System.out.println("提交成功"); //提交事务 sqlSession.commit(); } sqlSession.close(); } }
# 注意事项
-
如果
Mapper.xml
创建在java
目录里而不是resouces
里,需要在Maven配置里配置过滤,否则会保错1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
<resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources>
-
当需要传入参数进行查询而不是传入实体类,有两种方法
-
第一种:直接在方法中传递参数。
在接口方法的参数前加
@Param
属性,sql语句编写的时候,直接取@Param
中设置的值即可,不需要单独设置参数类型1 2 3 4 5 6 7
User selectUserByNP(@Param("username") String username,@Param("pwd") String pwd); /* <select id="selectUserByNP" resultType="top.lbqaq.pojo.User"> select * from user where name = #{username} and pwd = #{pwd} </select> */
-
第二种:通过map来传递参数。(推荐)
在接口创建时就直接使用map作为参数
1
int addUser2(Map<String,Object> map);
在写sql时直接设置入参为
map
即可1 2 3 4
<!--传递map的key--> <insert id="addUser2" parameterType="map"> insert into user (id,name,pwd) values (#{userid},#{userName},#{passWord}) </insert>
-
# 配置解析
# 核心配置
MyBatis官方给出的配置项有以下这么多:
|
|
我们需要关注的就是properties(属性)
、settings(设置)
、typeAliases(类型别名)
、plugins(插件)
、environments(环境配置)
、mappers(映射器)
这几项配置,其余的可以忽略(基本用不到)
# properties
数据库这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。
说白了,就是能将数据库那些配置项从核心配置中剥离出来,降低耦合。
# 用法
-
在
resources
目录下创建db.properties
,填入下面的内容1 2 3 4
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis username=root password=root
-
在配置xml中导入
1 2 3 4 5 6
<!--引用外部配置文件--> <properties resource="db.properties"> <!--外部文件优先级大于内部(下面)--> <property name="username" value="root"/> <property name="password" value="123"/> </properties>
正常情况下,只需要用
<properties resource="db.properties"/>
这短短一句就行了,上面的代码是用来测试优先级的
# settings
这里存放的是MyBatis的一些设置项,具体的内容在官方文档上可以查看
常用的设置项有以下几个:
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名A_COLUMN 映射到经典 Java 属性名 aColumn。 | true | false | False |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
# typeAliases
类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
也就是在resultType
和parameterType
中不需要写那么长的类名了
# 用法
|
|
有两种方法,第一种就是手动指定别名,第二种就是通过package
标签自动扫描包下所有的类
在第二种方法下,默认别名是类名全小写(当然首字母大写也同样可用)
如果在类前加上@Alias
注解,则别名就为手动设定的内容。
# plugins
这里是配置mybatis的插件的地方,常用的插件有以下几个:
这里是就不展开这些插件了,以后有需要再看
# environments
配置MyBatis的多套运行环境,将SQL映射到多个不同的数据库上,必须指定其中一个为默认运行环境(通过default指定)。
|
|
transactionManager
: 事务管理器 。有两个选项:- JDBC:正常的
- MANAGED:几乎啥都不干
dataSource
:如何使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。有三个选项:- unpooled:这个数据源的实现只是每次被请求时打开和关闭连接。
- pooled:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来 , 这是一种使得并发 Web 应用快速响应请求的流行处理方式。
- jndi:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
# mappers
用于告诉MyBatis我们写的xml文件到底在哪,每个xml文件都需要绑定!
常用的有以下几种方法:
|
|
|
|
|
|
# ResultMap
# 属性名和字段名不一致
在实际开发中,会遇到类的属性名和数据库的字段名不一致,这时MyBatis的自动匹配将无法产生作用,这时可以使用ResultMap
来解决这个问题。
|
|
# 多对一
在开发时常常会有这样的需求,比如多个学生都被一个老师教,下面将对该实例具体展开来了解多对一如何处理。
# 环境搭建
-
添加Lombok插件
-
引入Maven依赖
1 2 3 4 5 6 7 8 9
<dependencies> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> <scope>provided</scope> </dependency> </dependencies>
-
编写实体类
1 2 3 4 5 6 7 8 9 10
@Data public class Student { private int id; private String name; /** * 学生需要关联一个老师 */ private Teacher teacher; }
1 2 3 4 5
@Data public class Teacher { private int id; private String name; }
# 按查询嵌套处理
思路:
-
获取所有学生的信息
-
根据获取的学生信息的老师ID->获取该老师的信息
-
思考问题,这样学生的结果集中应该包含老师,该如何处理呢,数据库中我们一般使用关联查询?
- 做一个结果集映射:StudentTeacher
- StudentTeacher结果集的类型为 Student
- 学生中老师的属性为teacher,对应数据库中为tid。 多个 [1,…)学生关联一个老师=> 一对一,一对多
- 查看官网找到:association – 一个复杂类型的关联;使用它来处理关联查询
|
|
注意点:
|
|
这个还是比较难以理解的,一般我选择用第二种方法
# 按结果嵌套处理
|
|
这种就比较好理解了,难度主要在sql部分,好在之前数据库学的还行,这种方法还是比较适合我。
# 一对多
一个老师会教多个学生。
# 环境搭建
|
|
|
|
# 按结果嵌套查询
|
|
JavaType
和ofType
都是用来指定对象类型的。
JavaType
是用来指定pojo
中属性的类型。
ofType
指定的是映射到list集合属性中pojo
的类型。
# 按查询嵌套
|
|
# 日志工厂
如果一个数据库相关的操作出现了问题,我们可以根据输出的SQL语句快速排查问题。可见打印出SQL语句是十分重要的。(还记得之前项目综合实践为了搞日志忙了半天,结果还是没搞出来😭)
# 标准日志
在MyBatis里自带了通过控制台打印的日志功能,如果项目并不是很复杂,用它就足够了。
只需要在设置里设置启用就可以了。
|
|
# log4j
log4j相比标准日志,那不知道高到哪里去了,具体的功能我就不写了,毕竟一百度就能出来了。
使用步骤:
-
导入log4j的包
1 2 3 4 5
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
在
resources
目录下新建log4j.properties
,并填入以下配置文件1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#将等级为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/log.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=DEBUG
-
在MyBatis里启用
1 2 3
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
-
在程序中使用Log4j进行输出
1 2 3 4 5 6 7 8 9 10 11 12 13
import org.apache.log4j.Logger; import org.junit.Test; public class UserMapperTest { static Logger logger = Logger.getLogger(UserMapperTest.class); @Test public void testLog4j(){ logger.info("info:进入了testLog4j"); logger.debug("debug:进入了testLog4j"); logger.error("error:进入了testLog4j"); } }
# 使用注解开发
MyBatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。
而到MyBatis 3提供了新的基于注解的配置。不幸的是,Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建。
在注解开发中,主要使用这几个注解:@select ()
、@update ()
、@Insert ()
、@delete ()
举例:
|
|
改造MybatisUtils
工具类的getSession()
方法,重载实现。
|
|
@Param注解用于给方法参数起一个名字。以下是总结的使用原则:
- 在方法只接受一个参数的情况下,可以不使用@Param。
- 在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。
- 如果参数是 JavaBean , 则不能使用@Param。
- 不使用@Param注解时,参数只能有一个,并且是JavaBean。
# 动态SQL
动态SQL指的是根据不同的查询条件 , 生成不同的sql语句。
主要使用这几个标签:if
、choose
、where
、set
、foreach
|
|
# SQL片段
有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
提取SQL片段:
|
|
引用SQL片段:
|
|
注意:
①、最好基于 单表来定义 sql 片段,提高片段的可重用性
②、在 sql 片段中不要包括 where
# 缓存
# 简介
1、什么是缓存 [ Cache ]?
- 存在内存中的临时数据。
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
2、为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
3、什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据。
# MyBatis缓存
-
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
-
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
-
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
- 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
# 一级缓存
一级缓存也叫本地缓存:
- 与数据库同一次会话期间查询到的数据会放在本地缓存中。
- 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
# 一级缓存失效的四种情况
- sqlSession不同
- sqlSession相同,查询条件不同
- sqlSession相同,两次查询之间执行了增删改操作!
- sqlSession相同,手动清除一级缓存
# 二级缓存
-
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
-
基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
-
工作机制
-
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map)中;
# 使用方法
-
开启全局缓存
mybatis-config.xml
1
<setting name="cacheEnabled" value="true"/>
-
去每个
mapper.xml
中配置使用二级缓存,这个配置非常简单。1 2 3 4 5 6 7 8 9
<cache/> 官方示例 <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> 这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。