目录
- 1. 简介
- 2.初涉Mybatis
- 3.Mybatis的CRUD简单实现
- 4.配置解析
- 5.拓展:当属性名和列名不一致
- 6.日志
- 7.分页
- 8.注解开发
- 9.Lombok
- 10.复杂查询
- 11.动态SQL
- 12.Mybatis缓存
1. 简介
1.1什么是Mybatis
- MyBatis 是一款优秀的持久层框架
- 它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
1.2 如何获得Mybatis
-
获得maven仓库:
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency>
1.3 使用Mybatis的好处:
- 使用简单,传统的JDBC代码太复杂
- 简单易学:没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件。
- 灵活
- 解除sql与程序代码的耦合
- 提供映射标签,支持对象与数据库的orm字段关系映射。
- 提供对象关系映射标签,支持对象关系组建维护。
- 提供xml标签,支持编写动态sql。
2.初涉Mybatis
2.1环境搭建
-
使用navicat创建一个数据库
-
新建项目
-
导入依赖
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency>
2.2、创建一个模块(项目)
-
编写mybatis的核心配置文件
<?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?&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="18227022334a"/> </dataSource> </environment> </environments> <mappers> <mapper resource="Mapper/UserMapper.xml"/> </mappers> </configuration> <!--注:xml文件中使用&符号需要改成使用 & 代替-->
-
编写mybatis工具类(封装):
public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; //第一步:获取SqlSessionFactory对象 static { try { String resource = "mybatis-config.xml";//路径写对 InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //通过SqlSessionFactory对象获取SqlSession对象 //SqlSession:SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。 public static SqlSession getSqlSession(){return sqlSessionFactory.openSession();}//当传递一个true参数时表示自动提交事务 }
-
编写
-
编写实体类
//注;实体类的属性名必须与数据库的列名一致,否则无法进行匹配从而出现数据为空的现象,当然也可以在Mapper.xml配置中通过编写一个resultMap来进行映射,此时名字可以不相同 public class User { private int id; private String username; private String password; public User() { } public User(int id, String username, String password) { this.id = id; this.username = username; this.password = password; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
-
编写Mapper接口(Dao)
public interface UserMapper { public List<User> getUser(); }
-
编写Mapper配置文件
//注:每个Mapper文件都需要在mybatis-config.xml文件中去注册 //<mapper resource="Mapper/UserMapper.xml"/> //相当于Mapper的接口实现类 <?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:命名空间,指定要实现的Mapper接口,路径映射 <mapper namespace="com.study.dao.UserMapper"> //id:唯一匹配于Mapper接口的方法名 //resultType:返回类型,必须写全限定名称 <select id="getUser" resultType="com.study.pojo.User"> select * from user; </select> </mapper>
-
-
测试
public class UserMapperTest { @Test public void test(){ //获得sqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); //获取UserMapper的对象以调用接口内的方法 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //调用方法 List<User> users = userMapper.getUser(); //遍历结果集 for (User user : users) { System.out.println(user.getId() + " " + user.getUsername() + " " + user.getPassword()); } //关闭sqlSessoin sqlSession.close(); } }
2.3、使用Mybatis的三个重要类
-
SqlSessionFactoryBuilder:
可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情
-
SqlSessionFactory:
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例,最简单的就是使用单例模式或者静态单例模式
-
SqlSession:
SqlSession 的实例不是线程安全的,因此是不能被共享的,每次打开一个 SqlSession,记得关闭它, 这个关闭操作很重要
3.Mybatis的CRUD简单实现
3.1、通过id查找用户:
-
接口:
public User getUserById(int id);
-
Mapper配置文件
<select id="getUserById" resultType="com.study.pojo.User"> select * from user where id=#{id}; </select>
-
测试
SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.getUserById(1); sqlSession.close();
3.2、插入用户:
-
接口:
public void insertUser(User user);
-
Mapper配置文件
<insert id="insertUser"> insert into user values (#{id},#{username},#{password}); </insert>
-
测试
SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.insertUser(new User(5,"老六","1234")); sqlSession.commit(); sqlSession.close(); //注:在Mybatis中增删改查找都需要使用事务,必须手动提交事务如果没有配置自动管理事务的情况下
3.3、根据id更改用户名字:
-
接口:
public void insertUser(User user);
-
Mapper配置文件
<insert id="insertUser"> insert into user values (#{id},#{username},#{password}); </insert>
-
测试
//当有多个参数的时候,可以使用@Param()注解指定名字 //public void updateName(@Param("id") int id,@Param("username") String username); SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.updateName(3,"王五"); sqlSession.commit(); sqlSession.close();
3.4、 根据id删除用户:
-
接口:
public void insertUser(User user);
-
Mapper配置文件
<insert id="insertUser"> insert into user values (#{id},#{username},#{password}); </insert>
-
测试
SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.deleteUserById(5); sqlSession.commit(); sqlSession.close();
3.5、好用的Map
当接口中传递的对象有多个的时候,除了可以使用·@Param()注解之外,我们常常使用一个不算规范但是特别好用的方式,那就是传递Map
-
接口:
public void updateNameById(Map<String,Object> map);
-
mapper配置文件:
<update id="updateNameById" > update user set username=#{username} where id=#{id}; </update>
-
调用:
SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); Map<String,Object> map=new HashMap(); map.put("username","战三"); map.put("id",2); userMapper.updateNameById(map); sqlSession.commit(); sqlSession.close();
总结:
- 当接口只传递一个参数的时候:可以直接在mapper文件使用,且不需要与参数名字对应
- 当需要多个参数的时候:
- 如果有一个实体类正好与之对应,那么可以传递一个对象,然后在mapper文件中通过#{对象.属性}调用
- 通过使用注解的方式@Param()指定名字
- 通过传递一个Map
3.6、模糊查询
-
方式一:在java代码在传递“%”
userMapper.getUserLike("%张%");
-
方式二:在mapper文件的sql语句中实现
<select id="getUserLike" resultType="com.study.pojo.User"> select * from user where username like "%"#{name}"%" ; </select>
4.配置解析
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
- configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
4.1、environments 环境配置
MyBatis 可以配置成适应多种环境,不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
environments 元素定义了如何配置环境。
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<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>
<!--如果要切换环境只需要更改环境default,改成需要使用的环境的id-->
<environment id="test">
</environment>
</environments>
注意点:
- 默认使用的环境 ID(比如:default=”development”)。
- 每个 environment 元素定义的环境 ID(比如:id=”development”)。
- 事务管理器的配置(比如:type=”JDBC”)。
- 数据源的配置(比如:type=”POOLED”)。
4.2、properties(属性)
可以从外部的资源环境读取,也可以在内部定义,可以动态的替换环境中的配置信息
-
外部资源文件引入:
-
db.properties
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username=root password=11111111
-
引入:
<properties resource="db.properties"> </properties>
-
-
内部使用property定义:
<properties resource="org/mybatis/example/config.properties"> <property name="username" value="dev_user"/> <property name="password" value="F2Fa3!33TYyg"/> </properties>
设置好的属性可以动态替换:
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
注:如果同时使用了外部资源文件和内部定义的方式那么会使用外部资源文件。
4.3、typeAliases(类型别名)
类型别名可为 Java 类型设置一个缩写名字,降低冗余的全限定类名书写
-
方式一:使用具体的全限定类名
<typeAliases> <typeAlias alias="Author" type="domain.blog.Author"/> <typeAlias alias="Blog" type="domain.blog.Blog"/> </typeAliases>
注:设定之后在其他使用domain.blog.Author的地方就可以使用Author代替
-
方式二:使用实体类包名
<typeAliases> <package name="domain.blog"/> </typeAliases>
注:通过这种方式设定之后,在该包下所有的实体类的全限定类名可以使用该类名的首字母小写来代替,比如User使用user代替
总结:两种方式各有优劣,当类比较少的时候可以使用第一种方式,当类比较多的时候可以使用第二种方式,如果想在使用第二种方式的同时给特点的类设定指定名字,可以使用注解的方式,且如果同时使用了第一种方式和第二种方式那么使用两个别名都正确。
@Alias("author")
public class Author {}
4.4、settings(设置)
1.常用的一些设置:
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 |
true | false | false |
useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 | true | false | False |
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true | false | False |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
日志:
- STDOUT_LOGGING (mybatis默认)
- LOG4J (掌握)
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- SLF4J
- NO_LOGGING:
4.5、mappers(映射器)
-
作用:告诉 MyBatis 到哪里去找映射文件
-
实现方式:
-
方式一:resource(推荐)
<!-- 使用相对于类路径的资源引用 --> <mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <mapper resource="org/mybatis/builder/BlogMapper.xml"/> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers>
-
方式二:url(极不推荐)
<!-- 使用完全限定资源定位符(URL) --> <mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> <mapper url="file:///var/mappers/BlogMapper.xml"/> <mapper url="file:///var/mappers/PostMapper.xml"/> </mappers>
-
方式三:class
<!-- 使用映射器接口实现类的完全限定类名 --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> <mapper class="org.mybatis.builder.BlogMapper"/> <mapper class="org.mybatis.builder.PostMapper"/> </mappers>
注:
- 接口必须和mapper配置文件在同一个包下
- 接口和mapper配置文件的名字必须相同
-
方式四:name
<!-- 将包内的映射器接口实现全部注册为映射器 --> <mappers> <package name="org.mybatis.builder"/> </mappers>
注:
- 接口必须和mapper配置文件在同一个包下
- 接口和mapper配置文件的名字必须相同
-
4.6、其他(暂时了解)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)(有用)
- Mybatis PageHelper分页插件
- Mybatis通用Mapper插件
- Mybatis Plus插件
- 代码生成插件mybatis-generator
5.拓展:当属性名和列名不一致
假设存在一个数据库user表,字段如下:
一个User实体类如下:
public class User {
private int id;
private String username;
private String password;
}
那么当查询结果集为User类型的时候,会出现姓名为空的问题。
解决方式:
-
方式一:在sql语句中使用别名
select id ,name as usernaem,password from user;
-
方式二:resultMap结果映射
<resultMap id="userMap" type="user"> <id property="id" column="id"/> <result property="username" column="name"></result> <result property="password" column="password"></result> </resultMap> <select id="getUserAll" resultMap="userMap"> select * from user ; </select>
-
resultMap
元素是 MyBatis 中最重要最强大的元素。 -
ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
-
ResultMap
的优秀之处——你完全可以不用显式地配置它们,如上面的resultMap可以改为
<resultMap id="userMap" type="user"> <result property="username" column="name"></result> </resultMap>
即只需要显示的配置不匹配的情况即可。
注:其实第一种方法使用别名的本质上还是使用了resultMap的映射,因为在这些情况下,MyBatis 会在幕后自动创建一个
ResultMap
,再根据属性名来映射列到 JavaBean 的属性上。 -
6.日志
6.1、日志工厂
-
当我们操作数据库出现错误时,需要借助一些手段进行排错
- 之前:sout,debug
- 现在:日志
-
日志类别:
- STDOUT_LOGGING(掌握)(默认的标准日志工厂)
- LOG4J(掌握)
- LOG4J2
- SLF4J
- JDK_LOGGING
- COMMONS_LOGGING
- NO_LOGGING
-
配置日志:
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
使用了日志之后,我们就可以查看到非常多的信息
6.2、Log4j
-
介绍:
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件等
- 我们也可以控制每一条日志的输出格式;
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
- 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
-
log4j:
-
导入log4j依赖
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
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.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%p:%c%n%m%l%n%m #文件输出的相关设置 log4j.appender.file=org.apache.log4j.FileAppender log4j.appender.file.File=d:/log4jFile/mybatis.log log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %F %p %m%n log4j.appender.file.MaxFileSize=10mb #控制日志的输出鉴别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
-
在mybatis-config中配置log4j的使用
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
-
7.分页
思考:为什么要分页
- 减少数据的处理量
7.1、 limit分页:
select * from user limit startIndex,pagesize;
select *from user limit pagesize; -- 当只有一个参数的时候,从第一个数据开始
7.2、 RowBounds分页(不推荐使用)
-
接口:
public List<User> getUser();
-
mapper.xml:
<select id="getUser" resultType="com.study.pojo.User"> select * from user ; </select>
-
实现:
SqlSession sqlSession = MybatisUtils.getSqlSession(); RowBounds rowBounds=new RowBounds(1,2); List<User> list = sqlSession.selectList("com.study.dao.UserMapper.getUser", null, rowBounds); for (Object user : list) { System.out.println(user.toString()); }
7.3、分页插件
使用PageHelper分页插件,https://pagehelper.github.io/,了解一下即可,如有需要再看使用文档。
8.注解开发
- 使用注解来映射简单语句会使代码显得更加简洁
- 但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪
- 如果需要做一些很复杂的操作,最好用 XML 来映射语句。
使用建议:除非是很简单的操作,否则尽量建议使用xml方式完成,一般而言我们都会使用xml。
8.1、常用的注解
-
@Insert:
插入记录的时候麻烦的一点是主键如何生成,对此基本上有三种方案,分别是手动指定(应用层)、自增主键(数据层单表)、选择主键(数据层多表)。(如想了解可以查)
@Insert("insert into user values (#{id},#{username},null)")
-
@Delete:
@Delete("delete from user where id=#{id};")
-
@Updata:
@Update(" update user set username=#{username} where id=#{id};")
-
@select:
@Select("select * from user")
-
@Param:
在接口中传递多个参数的时候可以指定
public void updateName(@Param("id") int id,@Param("username") String username);
注:
- 基本类型或者String类型需要加上
- 引用类型不需要加
- 如果只有一个基本类型的参数可以不加
- 在mapper.xml中使用的就是它指定的名字
-
@Results, @Result:
当使用select标签时,如果查询的字段与当前实体类不能进行很好的匹配那么需要我们进行一个映射
@Results(id = "userMap", value = { @Result(id=true, column = "id", property = "id"), @Result(column = "username", property = "username"), @Result(column = "passwd", property = "passwd"), }) @Select("SELECT * FROM t_user WHERE id=#{id}") User loadByIdResultMap(Long id);
-
@ResultMap:
如果以及存在一个@Results,那么可以通过@ResultMap指定id名字去引用它
@ResultMap("userMap") @Select("SELECT * FROM t_user WHERE id=#{id}") User loadByIdResultMapReference(Long id);
8.2、使用注解开发
-
接口:
public interface UserMapper { @Select("select * from user") public List<User> getUser(); @Update(" update user set username=#{username} where id=#{id};") public void updateName(@Param("id") int id,@Param("username") String username); @Insert("insert into user values (#{id},#{username},null)") public void insertUser(User user); @Delete("delete from user where id=#{id};") public void deleteUserById(int id); }
-
mybatis-config.xml注册接口:
<mappers> <mapper class="com.study.dao.UserMapper"/> </mappers>
-
测试:
SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> user = userMapper.getUser(); //userMapper.insertUser(new User(6,"随便","123123")); //userMapper.updateName(2,"还行"); //userMapper.deleteUserById(1); for (User user1 : user) { System.out.println(user1); } sqlSession.commit(); sqlSession.close();
本质:反射机制实现
底层:动态代理
8.3、关于#{}和${}的比较
- 前者是占位,在使用时会将传入的字符串加上引号当做一个整体,${}是拼接,在使用的时候会将传入的字符串不做处理直接拼接
- 使用#{}时会进行预编译可以防止sql注入,使用${}不会预编译不可以防止sql注入
- 使用时#{}是编译好SQL后语句再去取值,${}取值以后再去编译SQL语句
- 建议:一般能用#{}就不用${}
8.4、当注解开发和xml开发同时使用
注:虽然注解和xml配置文件可以同时使用,但是如果在接口中的同一个方法上既使用了注解,有在xml文件中进行了配置,也就是对同一个方法同时使用了注解配置文件两种方式,那么程序会报错。
9.Lombok
Lombok项目是一个java库,它可以自动插入到编辑器和构建工具中,增强java的性能。不需要再写getter、setter或equals方法,只要有一个注解,你的类就有一个功能齐全的构建器、自动记录变量等等.
-
常用注解:
- Data:整合了Getter、Setter、ToString、EqualsAndHashCode、无参构造函数注解。
- Getter:快速构建Getter方法。
- Setter:快速构建Setter方法。
- ToString:快速将当前对象转换成字符串类型,便于log
- EqualsAndHashCode:快速进行相等判断
- NonNull:判断变量(对象)是否为空。
- AllArgsConstructor:快速构建全部参数的构造函数
- NoArgsConstructor:快速构建无参构造函数
-
Lombok的使用:
-
在IDEA中安装Lombok插件
-
在项目中导入lombok的jar包
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> </dependency>
-
使用:
@Data @AllArgsConstructor @NoArgsConstructor public class Persosn { private String name; private int age; }
注:由于使用@Data只含有无参构造函数,使用要结合@AllArgsConstructor使用,但是使用了@AllArgsConstructor之后@Data的无参构造函数就会消失,使用需要再搭配@NoArgsConstructor使用。
-
注:缺点:无法承载有各种参数的构造函数,但是我们可以手动的去添加。
10.复杂查询
10.1、模拟场景环境搭建
- 新建项目,导入相关依赖
- 配置文件的编写
- 新建实体类Student和Teacher
- 建立对应的Mapper接口
- 建立对应的Mapper配置文件
- 注册配置文件
- 测试环境搭建是否成功
10.2、多对一实际应用:关联
比如多个学生对应一个老师,就是多对一
实体类如下:
@Data
public class Student {
private int id;
private String name;
private Teacher teacher;//多个学生一个老师
}
@Data
public class Teacher {
private int id;
private String name;
}
实现关键:association
10.2.1、嵌套查询
<resultMap id="Student_t" type="student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
</resultMap>
<select id="getStudent" resultMap="Student_t">
select * from student ;
</select>
<select id="getTeacher" resultType="teacher">
select * from teacher where id=#{id}
</select
通过嵌套一个子查询的方式,通过学生的tid去找到对应的老师
10.2.2、嵌套结果
<resultMap id="Student_t2" type="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
</association>
</resultMap>
<select id="getStudent2" resultMap="Student_t2">
select s.id sid,s.name sname,t.id tid ,t.name tname from student s,teacher t where s.tid=t.id;
</select>
通过对结果集直接进行映射
10.3、一对多实际应用:集合
比如一个老师有多个学生,对于老师而言就是一对多
实体类如下:
@Data
public class Student {
private int id;
private String name;
private int tid;
}
@Data
public class Teacher {
private int id;
private String name;
private List<Student> students;
}
实现关键:collection
10.3.1、嵌套查询
<resultMap id="teacher_s1" type="teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<collection property="students" ofType="student" column="id" select="getStudent" javaType="ArrayList"/>
</resultMap>
<select id="getTeacher" resultMap="teacher_s1">
select * from teacher t;
</select>
<select id="getStudent" resultType="student">
select * from student where tid=#{id};
</select>
10.3.2、嵌套结果
<resultMap id="teacher_s2" 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 column="tid" property="tid"/>
</collection>
</resultMap>
<select id="getTeacher1" resultMap="teacher_s2">
select s.id sid,s.name sname,t.id tid ,t.name tname from student s,teacher t where s.tid=t.id;
</select>
10.4、小结
- 关联:association
- 集合:collection
- javaType:用来指定实体类中的属性的类型
- ofType:用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
附:面试高频问题;
- MySql引擎
- InnoDB底层原理
- 索引
- 索引优化
11.动态SQL
11.1、简单介绍
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
种类:
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
11.2、实例项目的搭建
-
在数据库中根据以下实体类创建blog表
@Data public class Blog { private String id; private String title; private String author; private Date createTime; private int views; }
拓展:实体类中的id类型是String,在具体的一个项目中其实我们可以使用UUID这个类来随机生成一个唯一的ID,工具类简单封装如下:
public class IDUtils { public static String getId(){ return UUID.randomUUID().toString().replace("-",""); } }
-
新建项目,导入相关依赖
-
配置文件的编写
<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
注:开启这个设置可以自动将数据库的下划线映射到实体类的驼峰命名属性
-
新建实体类Blog
-
建立对应的Mapper接口
-
建立对应的Mapper配置文件
-
注册配置文件
-
测试环境搭建是否成功
11.3动态SQL的使用
11.3.1、if
- if提供了条件判断的功能,可以根据情况决定是否要追加语句。
<select id="queryBlog" resultType="blog">
select * from blog where 1=1
<if test="id != null">
and id=#{id}
</if>
<if test="title != null">
and title =#{title}
</if>
<if test="author!='李四'">
and author =#{author}
</if>
<if test="views > 800 ">
and views>#{views}
</if>
</select>
注:第一个where条件后面加一个1=1是为了方便后面的每一个判断条件追加sql的第一个能写and
11.3.2、choose (when, otherwise)
- 有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
<select id="queryBlog" resultType="blog">
select * from blog
<where>
<choose>
<when test="id != null">
id=#{id}
</when>
<when test="title != null">
and title =#{title}
</when>
<when test="author!='李四'">
and author =#{author}
</when>
<otherwise>
and views>#{views}
</otherwise>
</choose>
</where>
</select>
- 注:当同时满足多个when的时候,按照顺序选择拼接,比如第一个when满足那么即是后面的满足了也不会拼接,然后都不满足那么拼接otherwise里面的内容,另外otherwise不是必须的
11.3.3、trim (where, set)
-
where:where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
-
set:set 元素可以用于动态包含需要更新的列,忽略其它不更新的列
<update id="updateAuthorIfNecessary"> update Author <set> <if test="author != null">author=#{author},</if> <if test="title != null">title=#{title},</if> <if test="views >= 0">views=#{views}</if> </set> </update>
-
trim:
-
trim包含的属性;
- prefix:自定义前缀
- prefixOverrides:需要被移除的前缀
- suffix:自定义后缀
- suffixOverrides:需要被移除的后缀
-
可以自定义标签的替换方式,比如与where等同效果的格式如下:
<trim prefix="WHERE" prefixOverrides="AND |OR "> ... </trim>
prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。
-
与set等同效果的格式如下:
<trim prefix="SET" suffixOverrides=","> ... </trim>
-
11.3.4、foreach
-
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。
-
可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
<select id="queryBlog" resultType="blog"> select * from blog <where> <foreach collection="names" item="name" open="author in (" close=")" separator=","> #{name} </foreach> </where> </select>
测试:
@Test public void test() { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class); ArrayList<String> names = new ArrayList<>(); names.add("张三"); names.add("李四"); Map<String,List> map=new HashMap<>(); map.put("names",names); List<Blog> blogs = blogMapper.queryBlog(map); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close(); }
11.3.5、sql片段
sql片段的作用就是可以将一部分sql代码抽取出来,然后可以对他进行一个复用。
<select id="queryBlog" resultType="blog">
select * from blog
<where>
<include refid="choose"></include>
</where>
</select>
<sql id="choose">
<choose>
<when test="id != null">
id=#{id}
</when>
<when test="title != null">
and title =#{title}
</when>
<when test="author!='李四'">
and author =#{author}
</when>
</choose>
</sql>
注意事项:
- 尽量不要使sql片段太过复杂
11.3.6、小结
动态sql本质上就是拼接sql语句
建议:
- 我们可以在mysql中去写写出我们需要的sql语句,然后再去拼接
12.Mybatis缓存
12.1、简介
- 什么是缓存
- 存储在内存中的临时数据
- 将经常查询的信息从内存中复制一份到缓存,当再次查找时就直接从缓存中查找,从而提高效率,解决高并发的性能问题
- 使用缓存的好处
- 减少数据库的交互次数,减小系统的开销,提高效率
- 什么样的数据库可以使用缓存
- 查询比较频繁并且不经常改动的数据库
12.2、Mybatis缓存
mybatis本身有两种缓存,分别为一级缓存和二级缓存
-
一级缓存:
- MyBatis默认开启了一级缓存,一级缓存是在 SqlSession 层面进行缓存的。即,同一个 SqlSession ,多次调用同一个 Mapper 和同一个方法的同一个参数,只会进行一次数据库查询,然后把数据缓存到缓冲中,以后直接先从缓存中取出数据,不会直接去查数据库。
-
二级缓存:
- 二级缓存需要手动开启,他是基于namespce级别的缓存
- 为了提高拓展性,mybatis定义了缓存接口Cache,我们可以通过该接口自定义二级缓存
12.3、一级缓存
- 一级缓存也叫本地缓存:SqlSession
- 在同一个SqlSession之间查询到的数据会放到本地的缓存中
- 之后如果想要获取数据自己从本地缓存中取
测试步骤:
-
开启日志
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
-
测试在同一个SqlSession期间查询相同的数据
-
查看日志输出
缓存失效的情况:
-
查询不一样的数据
-
增删改操作,由于可能会改变原来的数据,所有缓存也会失效
-
查询不同的Mapper.xml
-
手动清除缓存
sqlSession.clearCache();
-
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
-
缓存不会定时进行刷新(也就是说,没有刷新间隔)
小结:一级缓存是默认开启的,也关闭不掉,只能手动清理或者设置,一级缓存其实就相当于一个Map
12.4、二级缓存
- 二级缓存也叫全局缓存,它的出现是为了解决一级缓存的作用域太低
- 它是基于namespace级别的缓存,一个命名空间,对应一个二级缓存
- 机制:
- 一个SqlSession查询一条数据,这个数据会被放到一级缓存中;
- 当这个SqlSession断开的时候,一级缓存就会消失,为了能够继续保存,我们就产生了二级缓存
- 当其他的SqlSession查询数据时,就可以先从二级缓存中查询
- 不同的mapper.xml文件查出的数据会放到自己的缓存(map)之中
测试步骤:
-
开启二级缓存设置;
<setting name="cacheEnabled" value="true"/> <!--注:二级缓存是默认开启的,但是我们一般会显示的写出来-->
-
在要使用二级缓存的mapper.xml配置文件中开启缓存:
<!--直接开启--> <cache/> <!--当然还可以给缓存设置一些属性--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> <!--该缓存的 清除缓存策略:FIFO(最近最少使用) 刷新间隔:60s 最多存储:结果对象或列表的 512 个引用 返回的对象被认为是只读的 -->
-
测试
小结:
-
在使用二级缓存的时候可能会报错:
Error serializing object. Cause: java.io.NotSerializableException
解决:原因是没有将实体类序列化,所以直接将类序列化即可
-
在mapper.xml文件中,对于查找标签都可以手动设置是否开启缓存
useCache="true"
-
缓存清除的策略:
LRU
– 最近最少使用:移除最长时间不被使用的对象。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
-
所有的数据都会先存到一级缓存,只有当会话提交或者关闭才会提交到二级缓存
12.5、缓存原理
12.6、自定义缓存(了解)
注:一般可以采用redis来做缓存!!