狂神说Java Mybatis笔记
参考:https://blog.csdn.net/ddddeng_/article/details/106927021
MyBatis
1、简介
1.1 什么是Mybatis
MyBatis 是一款优秀的持久层框架;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
1.2 持久化
持久化就是将程序的数据在持久状态和瞬时状态转化的过程。
内存:断电即失。数据库(Jdbc),io文件持久化。
为什么要持久化?
有一些对象,不能让他丢掉。内存太贵
1.3 持久层
Dao层、Service层、Controller层
完成持久化工作的代码块
层界限十分明显
1.4 为什么需要MyBatis
帮助程序员将数据存入到数据库中,方便,传统的JDBC代码太复杂了,简化,框架,自动化,不用MyBatis也可以,技术没有高低之分
优点:
简单易学,灵活
sql和代码的分离,提高了可维护性。
提供映射标签,支持对象与数据库的orm字段关系映射
提供对象关系映射标签,支持对象关系组建维护
提供xml标签,支持编写动态sql
2、第一个Mybatis程序
思路:搭建环境 --> 导入MyBatis --> 编写代码 --> 测试
2.1 搭建环境
新建项目
创建一个普通的maven项目
删除src目录 (就可以把此工程当做父工程了,然后创建子工程)
导入maven依赖
<!--导入依赖-->
<dependencies> <!--mysqlq驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.12</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
一般情况下,我们用到的资源文件(各种xml,properites,xsd文件等)都放在src/main/resources下面,利用maven打包时,maven能把这些资源文件打包到相应的jar或者war里。
有时候,比如mybatis的mapper.xml文件,我们习惯把它和Mapper.java放一起,都在src/main/java下面,这样利用maven打包时,就需要修改pom.xml文件,来把mapper.xml文件一起打包进jar或者war里了,否则,这些文件不会被打包的。(maven认为src/main/java只是java的源代码路径)。
添加build
<build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> <ild>
创建一个Module
2.2 创建一个模块(可能会报中文的错误,把utf-8改为utf8)
编写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核心配置文件--> <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?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> </configuration>
编写mybatis工具类
//sqlSessionFactory --> sqlSession public class MybatisUtils { static SqlSessionFactory sqlSessionFactory = null; 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(); } }
2.3 编写代码
实体类
Dao接口
public interface UserDao { public List<User> getUserList(); }
接口实现类 (由原来的UserDaoImpl转变为一个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=绑定一个指定的Dao/Mapper接口--> <mapper namespace="com.kuang.dao.UserDao"> <select id="getUserList" resultType="com.kuang.pojo.User"> select * from USER </select> </mapper>
编写实体类
package com.bupt.pojo; import lombok.AllArgsConstructor; import lombok.Data; @Data @AllArgsConstructor public class User { private int id; private String username; }
测试
import com.bupt.dao.UserDao; import com.bupt.pojo.User; import com.bupt.utils.MybatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.HashMap; import java.util.List; public class test { @Test public void test01(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); User userById = mapper.getUserById(1); System.out.println(userById); sqlSession.close(); } //增删改必须提交事务 @Test public void testInsert(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); int i = mapper.addUser(new User(2, "梨花")); if(i > 0){ System.out.println("插入成功"); } sqlSession.commit(); sqlSession.close(); } @Test public void updateUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); mapper.updateUser(new User(2,"刘雷")); sqlSession.commit(); sqlSession.close(); } @Test public void testDelete(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); int i = mapper.deleteUser(30); sqlSession.commit(); sqlSession.close(); } //实现模糊查询 @Test public void testLike(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); List<User> userLike = mapper.getUserLike("%张%"); for (User user:userLike){ System.out.println(user); } sqlSession.close(); } @Test public void testLike1(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); List<User> zhang = mapper.getUserLike1("张"); for (User user:zhang){ System.out.println(user); } sqlSession.close(); } @Test public void testMap(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); HashMap<String, Object> map = new HashMap<>(); map.put("id",4); map.put("username","王虎"); map.put("birthday","2021-03-27"); map.put("sex",1); map.put("address","北京"); int i = mapper.insertUser(map); sqlSession.commit(); sqlSession.close(); } }
注意点:
org.apache.ibatis.binding.BindingException: Type interface com.kuang.dao.UserDao is not known to the MapperRegistry.
MapperRegistry是什么?
核心配置文件中注册mappers
junit测试
@Test public void test(){ //1.获取SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); //2.执行SQL // 方式一:getMapper UserDao userDao = sqlSession.getMapper(UserDao.class); List<User> userList = userDao.getUserList(); for (User user : userList) { System.out.println(user); } //关闭sqlSession sqlSession.close(); }
可能会遇到的问题:
- 配置文件没有注册
- 绑定接口错误
- 方法名不对
- 返回类型不对
- Maven导出资源问题
3、CURD
1. namespace
namespace中的包名要和Dao/Mapper接口的包名一致
2. select
选择,查询语句;
id:就是对应的namespace中的方法名;
resultType : Sql语句执行的返回值;
parameterType : 参数类型;
编写接口
public interface UserMapper { //查询所有用户 public List<User> getUserList(); //插入用户 public void addUser(User user); }
编写对应的mapper中的sql语句
<insert id="addUser" parameterType="com.kuang.pojo.User">
insert into user (id,name,password) values (#{id}, #{name}, #{password})
</insert>
测试
@Test public void test2() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = new User(3,"黑子","666"); mapper.addUser(user); //增删改一定要提交事务 sqlSession.commit(); //关闭sqlSession sqlSession.close(); }
注意:增删改查一定要提交事务:
sqlSession.commit();
增删改查全部流程实现
配置依赖和让资源文件能不导出
<dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.15</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> </resource> </resources> </build>
db.properties
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/student?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC username=root password=root
mybatis-config.xml进行核心配置
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 <!--configuration核心配置文件--> 6 <configuration> 7 <properties resource="db.properties"/> 8 <environments default="development"> 9 <environment id="development"> 10 <transactionManager type="JDBC"/> 11 <dataSource type="POOLED"> 12 <property name="driver" value="${driver}"/> 13 <property name="url" value="${url}"/> 14 <property name="username" value="${username}"/> 15 <property name="password" value="${password}"/> 16 </dataSource> 17 </environment> 18 </environments> 19 <mappers> 20 <mapper resource="com/bupt/dao/UserDaoMapper.xml"></mapper> 21 </mappers> 22 </configuration>
配置类MybatisUtils 获取sqlsession
1 package com.bupt.utils; 2 3 import org.apache.ibatis.io.Resources; 4 import org.apache.ibatis.session.SqlSession; 5 import org.apache.ibatis.session.SqlSessionFactory; 6 import org.apache.ibatis.session.SqlSessionFactoryBuilder; 7 8 import java.io.IOException; 9 import java.io.InputStream; 10 11 12 public class MybatisUtils { 13 static SqlSessionFactory factory = null; 14 static { 15 String resource = "mybatis-config.xml"; 16 try { 17 InputStream resourceAsStream = Resources.getResourceAsStream(resource); 18 factory = new SqlSessionFactoryBuilder().build(resourceAsStream); 19 20 } catch (IOException e) { 21 e.printStackTrace(); 22 } 23 } 24 public static SqlSession getSqlSession(){ 25 return factory.openSession(); 26 } 27 }
实体类(默认和数据库对应)
1 package com.bupt.pojo; 2 3 import lombok.AllArgsConstructor; 4 import lombok.Data; 5 6 @Data 7 @AllArgsConstructor 8 public class User { 9 private int id; 10 private String username; 11 12 }
增删改查的接口UserDao
1 package com.bupt.dao; 2 3 import com.bupt.pojo.User; 4 5 import java.util.List; 6 import java.util.Map; 7 8 public interface UserDao { 9 10 //查询 11 User getUserById(int id);//查询 12 13 //insert一个用户 14 int addUser(User user); 15 16 //更新一个用户 17 int updateUser(User user); 18 19 //删除 20 int deleteUser(int id); 21 22 int insertUser(Map<String,Object> map); 23 24 //模糊查询 25 List<User> getUserLike(String name); 26 List<User> getUserLike1(String name); 27 }
增删改查的具体实现UserDaoMapper.xml
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <mapper namespace="com.bupt.dao.UserDao"> 6 7 <select id="getUserById" resultType="com.bupt.pojo.User" parameterType="int"> 8 select * from user where id = #{id} 9 </select> 10 11 <!-- 对象中的属性可以直接取出来--> 12 <insert id="addUser" parameterType="com.bupt.pojo.User" > 13 insert into user (id, username) values(#{id},#{username}); 14 </insert> 15 16 <update id="updateUser" parameterType="com.bupt.pojo.User"> 17 update user set username=#{username} where id=#{id}; 18 </update> 19 20 <delete id="deleteUser" parameterType="int"> 21 delete from user where id=#{id}; 22 </delete> 23 24 <select id="getUserLike" parameterType="String" resultType="com.bupt.pojo.User"> 25 select * from user where username like #{name} 26 </select> 27 28 <select id="getUserLike1" parameterType="String" resultType="com.bupt.pojo.User"> 29 select * from user where username like "%"#{name}"%" 30 </select> 31 32 <insert id="insertUser" parameterType="map" > 33 insert into user (id,username,birthday,sex,address) values (#{id},#{username},#{birthday},#{sex},#{address}); 34 </insert> 35 36 </mapper>
别忘了在mybatis配置文件注册Mapper
<mappers> <mapper resource="com/bupt/dao/UserDaoMapper.xml"></mapper> </mappers>
进行测试
1 import com.bupt.dao.UserDao; 2 import com.bupt.pojo.User; 3 import com.bupt.utils.MybatisUtils; 4 import org.apache.ibatis.session.SqlSession; 5 import org.junit.Test; 6 7 import java.util.HashMap; 8 import java.util.List; 9 10 public class test { 11 @Test 12 public void test01(){ 13 SqlSession sqlSession = MybatisUtils.getSqlSession(); 14 UserDao mapper = sqlSession.getMapper(UserDao.class); 15 User userById = mapper.getUserById(1); 16 System.out.println(userById); 17 sqlSession.close(); 18 } 19 20 //增删改必须提交事务 21 @Test 22 public void testInsert(){ 23 SqlSession sqlSession = MybatisUtils.getSqlSession(); 24 UserDao mapper = sqlSession.getMapper(UserDao.class); 25 int i = mapper.addUser(new User(2, "梨花")); 26 if(i > 0){ 27 System.out.println("插入成功"); 28 } 29 sqlSession.commit(); 30 sqlSession.close(); 31 } 32 33 @Test 34 public void updateUser(){ 35 SqlSession sqlSession = MybatisUtils.getSqlSession(); 36 UserDao mapper = sqlSession.getMapper(UserDao.class); 37 mapper.updateUser(new User(2,"刘雷")); 38 sqlSession.commit(); 39 sqlSession.close(); 40 } 41 42 @Test 43 public void testDelete(){ 44 SqlSession sqlSession = MybatisUtils.getSqlSession(); 45 UserDao mapper = sqlSession.getMapper(UserDao.class); 46 int i = mapper.deleteUser(30); 47 sqlSession.commit(); 48 sqlSession.close(); 49 } 50 //实现模糊查询 51 @Test 52 public void testLike(){ 53 SqlSession sqlSession = MybatisUtils.getSqlSession(); 54 UserDao mapper = sqlSession.getMapper(UserDao.class); 55 List<User> userLike = mapper.getUserLike("%张%"); 56 for (User user:userLike){ 57 System.out.println(user); 58 } 59 sqlSession.close(); 60 } 61 62 @Test 63 public void testLike1(){ 64 SqlSession sqlSession = MybatisUtils.getSqlSession(); 65 UserDao mapper = sqlSession.getMapper(UserDao.class); 66 List<User> zhang = mapper.getUserLike1("张"); 67 for (User user:zhang){ 68 System.out.println(user); 69 } 70 sqlSession.close(); 71 } 72 73 @Test 74 public void testMap(){ 75 SqlSession sqlSession = MybatisUtils.getSqlSession(); 76 UserDao mapper = sqlSession.getMapper(UserDao.class); 77 HashMap<String, Object> map = new HashMap<>(); 78 map.put("id",4); 79 map.put("username","王虎"); 80 map.put("birthday","2021-03-27"); 81 map.put("sex",1); 82 map.put("address","北京"); 83 int i = mapper.insertUser(map); 84 sqlSession.commit(); 85 sqlSession.close(); 86 } 87 }
6. 万能Map
假设,我们的实体类,或者数据库中的表,字段或者参数过多,我们应该考虑使用Map!
UserMapper接口
//用万能Map插入用户
public void addUser2(Map<String,Object> map);
UserMapper.xml
<!--对象中的属性可以直接取出来 传递map的key-->
<insert id="addUser2" parameterType="map">
insert into user (id,name,password) values (#{userid},#{username},#{userpassword})
</insert>
测试
1 @Test 2 public void test3(){ 3 SqlSession sqlSession = MybatisUtils.getSqlSession(); 4 UserMapper mapper = sqlSession.getMapper(UserMapper.class); 5 HashMap<String, Object> map = new HashMap<String, Object>(); 6 map.put("userid",4); 7 map.put("username","王虎"); 8 map.put("userpassword",789); 9 mapper.addUser2(map); 10 //提交事务 11 sqlSession.commit(); 12 //关闭资源 13 sqlSession.close(); 14 }
- Map传递参数,直接在sql中取出key即可! 【parameter=“map”】
- 对象传递参数,直接在sql中取出对象的属性即可! 【parameter=“Object”】
- 只有一个基本类型参数的情况下,可以直接在sql中取到
- 多个参数用Map , 或者注解!
7. 模糊查询
模糊查询这么写?
Java代码执行的时候,传递通配符% %
<select id="getUserLike1" parameterType="String" resultType="com.bupt.pojo.User">
select * from user where username like #{name}
</select>
1 List<User> userList = mapper.getUserLike("%李%");
在sql拼接中使用通配符
1 <select id="getUserList2" resultType="com.bupt.pojo.User"> 2 select * from user where name like "%"#{name}"%" 3 </select>
目前程序的框架为
pom.xml
1 <?xml version="1.0" encoding="UTF8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 7 <groupId>org.example</groupId> 8 <artifactId>Mybatis</artifactId> 9 <packaging>pom</packaging> 10 <version>1.0-SNAPSHOT</version> 11 <modules> 12 <module>mybatis-1-crud</module> 13 </modules> 14 <build> 15 <resources> 16 <resource> 17 <directory>src/main/java</directory> 18 <includes> 19 <include>**/*.properties</include> 20 <include>**/*.xml</include> 21 </includes> 22 <filtering>true</filtering> 23 </resource> 24 </resources> 25 </build> 26 <properties> 27 <maven.compiler.source>8</maven.compiler.source> 28 <maven.compiler.target>8</maven.compiler.target> 29 </properties> 30 <dependencies> 31 <dependency> 32 <groupId>mysql</groupId> 33 <artifactId>mysql-connector-java</artifactId> 34 <version>5.1.48</version> 35 </dependency> 36 <dependency> 37 <groupId>org.mybatis</groupId> 38 <artifactId>mybatis</artifactId> 39 <version>3.5.4</version> 40 </dependency> 41 <!--junit--> 42 <dependency> 43 <groupId>junit</groupId> 44 <artifactId>junit</artifactId> 45 <version>4.12</version> 46 <scope>test</scope> 47 </dependency> 48 49 </dependencies> 50 51 </project>
mybatis-config.xml
<?xml version="1.0" encoding="UTF8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!--configuration核心配置文件--> <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/myschool?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/bupt/dao/UserMapper.xml"/> </mappers> </configuration>
实体类User
package com.bupt.pojo; import org.apache.ibatis.session.SqlSessionFactory; import java.util.Objects; public class User { private int id; private String name; private String pwd; public int getId() { return id; } public String getName() { return name; } public String getPwd() { return pwd; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } public void setPwd(String pwd) { this.pwd = pwd; } public User(int id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } public User() { } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return id == user.id && Objects.equals(name, user.name) && Objects.equals(pwd, user.pwd); } @Override public int hashCode() { return Objects.hash(id, name, pwd); } }
UserDao
package com.bupt.dao; import com.bupt.pojo.User; import java.util.HashMap; import java.util.List; import java.util.Map; public interface UserDao { //查询接口 List<User> getUserList(); User getUserById(int id); //插入接口 public void addUser(User user); //更新一个用户 public void updateUser(User user); //删除一个用户 public void deleteUser(int id); //插入用户 public void addUser2(Map<String, Object> map); //模糊查询用户 List<User> getUserList2(String name); }
UserMapper.xml
<?xml version="1.0" encoding="UTF8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--configuration核心配置文件--> <mapper namespace="com.bupt.dao.UserDao"> <select id="getUserList" resultType="com.bupt.pojo.User"> select * from USER; </select> <select id="getUserById" parameterType="int" resultType="com.bupt.pojo.User"> select * from USER where id=#{id}; </select> <insert id="addUser" parameterType="com.bupt.pojo.User"> insert into user (id,name,pwd) values (#{id},#{name},#{pwd}); </insert> <update id="updateUser" parameterType="com.bupt.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> <insert id="addUser2" parameterType="map"> insert into user(id,name) values(#{userid},#{username}) </insert> <select id="getUserList2" resultType="com.bupt.pojo.User"> select * from user where name like "%"#{name}"%" </select> </mapper>
MybatisUtils
1 package com.bupt.utils; 2 3 import org.apache.ibatis.io.Resources; 4 import org.apache.ibatis.session.SqlSession; 5 import org.apache.ibatis.session.SqlSessionFactory; 6 import org.apache.ibatis.session.SqlSessionFactoryBuilder; 7 8 import java.io.IOException; 9 import java.io.InputStream; 10 11 public class MybatisUtils { 12 static SqlSessionFactory build = null; 13 static { 14 String resources = "mybatis-config.xml"; 15 try { 16 InputStream resourceAsStream = Resources.getResourceAsStream(resources); 17 build = new SqlSessionFactoryBuilder().build(resourceAsStream); 18 19 } catch (IOException e) { 20 e.printStackTrace(); 21 } 22 } 23 public static SqlSession getSqlSession(){ 24 return build.openSession(); 25 } 26 }
经常碰到这样的面试题目:#{}和${}的区别是什么?
网上的答案是:#{}是预编译处理,$ {}是字符串替换。
mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
mybatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。
使用 #{} 可以有效的防止SQL注入,提高系统安全性。
sql注入指的是用户输入 or 1=1等变量导致原sql语句逻辑发生了变化,使得可以直接进入数据库,而使用预编译的方法可以让sql语句只把传入的数据当做参数进行处理
而不会改变原有sql的逻辑,preparestatement和#{}都是把变量预编译,防止sql注入。
SQL注入详解 - myseries - 博客园 (cnblogs.com)
4、配置解析
1. 核心配置文件
-
mybatis-config.xml
-
Mybatis的配置文件包含了会深深影响MyBatis行为的设置和属性信息。
1 configuration(配置) 2 properties(属性) 3 settings(设置) 4 typeAliases(类型别名) 5 typeHandlers(类型处理器) 6 objectFactory(对象工厂) 7 plugins(插件) 8 environments(环境配置) 9 environment(环境变量) 10 transactionManager(事务管理器) 11 dataSource(数据源) 12 databaseIdProvider(数据库厂商标识) 13 mappers(映射器)
2. 环境配置 environments
MyBatis 可以配置成适应多种环境
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境
学会使用配置多套运行环境!
MyBatis默认的事务管理器就是JDBC ,连接池:POOLED
3. 属性 properties
我们可以通过properties属性来实现引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。【db.poperties】
编写一个配置文件
db.properties
1 driver=com.mysql.cj.jdbc.Driver 2 url=jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC 3 username=root 4 password=root
在核心配置文件中引入
<!--引用外部配置文件--> <properties resource="db.properties"> <property name="username" value="root"/> <property name="password" value="root"/> </properties>
- 可以直接引入外部文件
- 可以在其中增加一些属性配置
- 如果两个文件有同一个字段,优先使用外部配置文件的
类型别名 typeAliases
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置.
意在降低冗余的全限定类名书写。
<!--可以给实体类起别名-->
<typeAliases> <typeAlias type="com.kuang.pojo.User" alias="User"/> </typeAliases>
也可以指定一个包,每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author,;若有注解,则别名为其注解值。见下面的例子:
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
在实体类比较少的时候,使用第一种方式。
如果实体类十分多,建议用第二种扫描包的方式。
第一种可以DIY别名,第二种不行,如果非要改,需要在实体上增加注解。
@Alias("author") public class Author { ... }
5. 设置 Settings
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
6. 其他配置
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins 插件
mybatis-generator-core
mybatis-plus
通用mapper
7. 映射器 mappers
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配置文件必须在同一个包下
方式三:使用包扫描进行注入
<mappers>
<package name="com.kuang.dao"/>
</mappers>
8. 作用域和生命周期
声明周期和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder:
一旦创建了SqlSessionFactory,就不再需要它了
局部变量
SqlSessionFactory:
说白了就可以想象为:数据库连接池
SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建一个实例。
因此SqlSessionFactory的最佳作用域是应用作用域(ApplocationContext)。
最简单的就是使用单例模式或静态单例模式。
SqlSession:
连接到连接池的一个请求
SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
用完之后需要赶紧关闭,否则资源被占用!
8、使用注解开发
8.1 面向接口开发
三个面向区别
面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性和方法;
面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现;
接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题,更多的体现就是对系统整体的架构;
8.2 使用注解开发
注解在接口上实现
@Select("select * from user")
List<User> getUsers();
需要在核心配置文件中绑定接口
<mappers>
<mapper class="com.kuang.dao.UserMapper"/>
</mappers>
增删改查
1 package com.bupt.dao; 2 3 import com.bupt.pojo.User; 4 import org.apache.ibatis.annotations.*; 5 6 import java.util.List; 7 import java.util.Map; 8 9 public interface UserDao { 10 11 User getUserById(int id); 12 List<User> getUserAll(Map<String,Integer> map); 13 14 @Select("select * from user") 15 List<User> getUsers(); 16 17 @Delete("delete from user where id=#{uid}") 18 int deleteUser(@Param("uid") int id); 19 20 @Insert("insert into user(id,username) values(#{id},#{username})") 21 int insertUser(User user); 22 23 @Update("update user set username=#{username} where id=#{id}") 24 int update(User user); 25 26 }
对于增删改需要把opensession的参数设置为true,这样就等价于commit操作了,否则无法commit,及时写了commit,
public static SqlSession getSqlSession(){ SqlSession sqlSession = build.openSession(true); return sqlSession; }
1 import com.bupt.dao.UserDao; 2 import com.bupt.pojo.User; 3 import com.bupt.utils.MybatisUtils; 4 import javafx.geometry.VPos; 5 import org.apache.ibatis.session.SqlSession; 6 import org.junit.Test; 7 8 import java.util.HashMap; 9 import java.util.List; 10 import java.util.Map; 11 import java.util.logging.Logger; 12 13 public class TestGetUserById { 14 15 @Test 16 public void test01(){ 17 Logger logger = Logger.getLogger(String.valueOf(UserDao.class)); 18 logger.info("info: 测试log4j"); 19 SqlSession sqlSession = MybatisUtils.getSqlSession(); 20 UserDao mapper = sqlSession.getMapper(UserDao.class); 21 User userById = mapper.getUserById(1); 22 System.out.println(userById); 23 sqlSession.close(); 24 } 25 26 @Test 27 public void test02(){ 28 SqlSession sqlSession = MybatisUtils.getSqlSession(); 29 UserDao mapper = sqlSession.getMapper(UserDao.class); 30 HashMap<String, Integer> stringHashMap = new HashMap<String, Integer>(); 31 stringHashMap.put("startIndex",1); 32 stringHashMap.put("pagesize",2); 33 List<User> userAll = mapper.getUserAll(stringHashMap); 34 for (User user:userAll){ 35 System.out.println(user); 36 } 37 sqlSession.close(); 38 } 39 40 @Test 41 public void test03(){ 42 SqlSession sqlSession = MybatisUtils.getSqlSession(); 43 UserDao mapper = sqlSession.getMapper(UserDao.class); 44 List<User> users = mapper.getUsers(); 45 for (User user:users){ 46 System.out.println(user); 47 } 48 sqlSession.close(); 49 } 50 51 @Test 52 public void test04(){ 53 SqlSession sqlSession = MybatisUtils.getSqlSession(); 54 UserDao mapper = sqlSession.getMapper(UserDao.class); 55 int i = mapper.deleteUser(2); 56 System.out.println(i); 57 sqlSession.close(); 58 } 59 @Test 60 public void test05(){ 61 SqlSession sqlSession = MybatisUtils.getSqlSession(); 62 UserDao mapper = sqlSession.getMapper(UserDao.class); 63 int i = mapper.insertUser(new User(3,"test")); 64 System.out.println(i); 65 sqlSession.close(); 66 } 67 68 @Test 69 public void test06(){ 70 SqlSession sqlSession = MybatisUtils.getSqlSession(); 71 UserDao mapper = sqlSession.getMapper(UserDao.class); 72 int i = mapper.update(new User(3,"test02")); 73 System.out.println(i); 74 sqlSession.close(); 75 } 76 }
测试
本质:反射机制实现
底层:动态代理
MyBatis详细执行流程
8.3 注解CURD
//方法存在多个参数,所有的参数前面必须加上@Param("id")注解
@Delete("delete from user where id = ${uid}")
int deleteUser(@Param("uid") int id);
关于@Param( )注解
基本类型的参数或者String类型,需要加上
引用类型不需要加
如果只有一个基本类型的话,可以忽略,但是建议大家都加上
我们在SQL中引用的就是我们这里的@Param()中设定的属性名
#{} 和 ${}
9、Lombok
Lombok项目是一个Java库,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样板代码。仅五个字符(@Data)就可以替换数百行代码从而产生干净,简洁且易于维护的Java类。
使用步骤:
在IDEA中安装Lombok插件
在项目中导入lombok的jar包
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> <scope>provided</scope> </dependency>
在程序上加注解
1 @Getter and @Setter 2 @FieldNameConstants 3 @ToString 4 @EqualsAndHashCode 5 @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor 6 @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog 7 @Data 8 @Builder 9 @SuperBuilder 10 @Singular 11 @Delegate 12 @Value 13 @Accessors 14 @Wither 15 @With 16 @SneakyThrows 17 @val
说明:
@Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String name; private String password; }
对于mybatis来说复杂的sql语句还是用配置文件来做,下面两个点是多对一和一对多的介绍。推荐使用联合嵌套查询
10、多对一处理
多个学生一个老师;
数据库表
1 CREATE TABLE `teacher` ( 2 `id` INT(10) NOT NULL, 3 `name` VARCHAR(30) DEFAULT NULL, 4 PRIMARY KEY (`id`) 5 ) ENGINE=INNODB DEFAULT CHARSET=utf8 6 7 INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师'); 8 9 CREATE TABLE `student` ( 10 `id` INT(10) NOT NULL, 11 `name` VARCHAR(30) DEFAULT NULL, 12 `tid` INT(10) DEFAULT NULL, 13 PRIMARY KEY (`id`), 14 KEY `fktid` (`tid`), 15 CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`) 16 ) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1'); 17 INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1'); 18 INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1'); 19 INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); 20 INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');
1. 测试环境搭建
- 导入lombok
- 新建实体类Teacher,Student
- 建立Mapper接口
- 建立Mapper.xml文件
- 在核心配置文件中绑定注册我们的Mapper接口或者文件 【方式很多,随心选】
- 测试查询是否能够成功
具体过程为
先在maven里面添加依赖
1 <dependencies> 2 <dependency> 3 <groupId>org.projectlombok</groupId> 4 <artifactId>lombok</artifactId> 5 <version>1.18.16</version> 6 </dependency> 7 <dependency> 8 <groupId>junit</groupId> 9 <artifactId>junit</artifactId> 10 <version>4.13</version> 11 <scope>test</scope> 12 </dependency> 13 <dependency> 14 <groupId>mysql</groupId> 15 <artifactId>mysql-connector-java</artifactId> 16 <version>8.0.15</version> 17 </dependency> 18 <dependency> 19 <groupId>org.mybatis</groupId> 20 <artifactId>mybatis</artifactId> 21 <version>3.5.4</version> 22 </dependency> 23 <dependency> 24 <groupId>log4j</groupId> 25 <artifactId>log4j</artifactId> 26 <version>1.2.12</version> 27 </dependency> 28 29 </dependencies>
配置文件
db.properties
1 driver=com.mysql.cj.jdbc.Driver 2 url=jdbc:mysql://localhost:3306/student?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC 3 username=root 4 password=root
log4j.properties
1 #将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 2 log4j.rootLogger=DEBUG,console,file 3 #控制台输出的相关设置 4 log4j.appender.console = org.apache.log4j.ConsoleAppender 5 log4j.appender.console.Target = System.out 6 log4j.appender.console.Threshold=DEBUG 7 log4j.appender.console.layout = org.apache.log4j.PatternLayout 8 log4j.appender.console.layout.ConversionPattern=[%c]-%m%n 9 #文件输出的相关设置 10 log4j.appender.file = org.apache.log4j.RollingFileAppender 11 log4j.appender.file.File=./log/rzp.log 12 log4j.appender.file.MaxFileSize=10mb 13 log4j.appender.file.Threshold=DEBUG 14 log4j.appender.file.layout=org.apache.log4j.PatternLayout 15 log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n 16 #日志输出级别 17 log4j.logger.org.mybatis=DEBUG 18 log4j.logger.java.sql=DEBUG 19 log4j.logger.java.sql.Statement=DEBUG 20 log4j.logger.java.sql.ResultSet=DEBUG 21 log4j.logger.java.sq1.PreparedStatement=DEBUG
mybatis-config.xml
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 <!--configuration核心配置文件--> 6 <configuration> 7 <properties resource="db.properties"/> 8 <!-- <settings>--> 9 <!--<!– <setting name="logImpl" value="log4j"/>–>--> 10 <!-- </settings>--> 11 <typeAliases> 12 <typeAlias type="com.bupt.pojo.Teacher" alias="Teacher"/> 13 <typeAlias type="com.bupt.pojo.Student" alias="Student"/> 14 <typeAlias type="com.bupt.pojo.Student2" alias="Student2"/> 15 <typeAlias type="com.bupt.pojo.Teacher2" alias="Teacher2"/> 16 </typeAliases> 17 <environments default="development"> 18 <environment id="development"> 19 <transactionManager type="JDBC"></transactionManager> 20 <dataSource type="POOLED"> 21 <property name="driver" value="${driver}"/> 22 <property name="url" value="${url}"/> 23 <property name="username" value="${username}"/> 24 <property name="password" value="${password}"/> 25 </dataSource> 26 </environment> 27 </environments> 28 29 <!-- 无论使用包扫描(package)还是使用class方法要注意一下 30 (1)接口和他的Mapper配置文件必须同名 31 (2)接口和他的Mapper配置文件必须在同一个包里面 32 --> 33 <!-- <mappers>--> 34 <!-- <mapper resource="com/bupt/dao/StudentMapper.xml"/>--> 35 <!-- <mapper class="com.bupt.dao.TeacherMapper"/>--> 36 <!-- </mappers>--> 37 38 <mappers> 39 <package name="com.bupt.dao"/> 40 </mappers> 41 </configuration>
utils包下的MybatisUtils文件
1 package com.bupt.utils; 2 3 import org.apache.ibatis.io.Resources; 4 import org.apache.ibatis.session.SqlSession; 5 import org.apache.ibatis.session.SqlSessionFactory; 6 import org.apache.ibatis.session.SqlSessionFactoryBuilder; 7 8 import java.io.IOException; 9 import java.io.InputStream; 10 11 public class MybatisUtils { 12 static InputStream resourceAsStream = null; 13 static SqlSessionFactory build = null; 14 static { 15 16 try { 17 String resource = "mybatis-config.xml"; 18 resourceAsStream = Resources.getResourceAsStream(resource); 19 build = new SqlSessionFactoryBuilder().build(resourceAsStream); 20 } catch (IOException e) { 21 e.printStackTrace(); 22 } 23 } 24 25 public static SqlSession getSqlSession(){ 26 SqlSession sqlSession = build.openSession(); 27 return sqlSession; 28 } 29 }
实体类Student和Teacher
package com.bupt.pojo; import lombok.Data; @Data public class Student { private int id; private String name; private Teacher teacher; }
1 package com.bupt.pojo; 2 3 import lombok.Data; 4 5 @Data 6 public class Teacher { 7 private int id; 8 private String name; 9 }
Mapper接口和对应的配置文件
StudentMapper
1 package com.bupt.dao; 2 3 import com.bupt.pojo.Student; 4 5 import java.util.List; 6 7 public interface StudentMapper { 8 //查询所有学生的信息,及其对应的老师的信息 9 public List<Student> getStudent(); 10 public List<Student> getStudent2(); 11 }
StudentMapper.xm;
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <mapper namespace="com.bupt.dao.StudentMapper"> 6 7 <select id="getStudent2" resultMap="StudentTeacher2"> 8 select s.id, s.name, t.name tname 9 from student s,teacher t 10 where s.tid = t.id 11 </select> 12 <resultMap id="StudentTeacher2" type="Student"> 13 <result property="id" column="id"/> 14 <result property="name" column="name"/> 15 <association property="teacher" javaType="Teacher" > 16 <result property="name" column="tname"/> 17 </association> 18 </resultMap> 19 20 21 <!-- 方式一 按照子查询的方式 --> 22 <select id="getStudent" resultMap="StudentTeacher"> 23 select * from student; 24 </select> 25 26 <resultMap id="StudentTeacher" type="Student"> 27 <result property="id" column="id"/> 28 <result property="name" column="name"/> 29 <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> 30 </resultMap> 31 32 <select id="getTeacher" resultType="Teacher"> 33 select * from teacher where id=#{tid} 34 </select> 35 </mapper>
TeacherMapper
1 package com.bupt.dao; 2 3 import com.bupt.pojo.Teacher; 4 import org.apache.ibatis.annotations.Param; 5 import org.apache.ibatis.annotations.Select; 6 7 public interface TeacherMapper { 8 9 @Select("select * from teacher where id=#{id}") 10 Teacher getTeacher(@Param("id") int id); 11 }
TeacherMapper.xml
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <mapper namespace="com.bupt.dao.TeacherMapper"> 6 7 </mapper>
测试
1 @Test 2 public void test01(){ 3 SqlSession sqlSession = MybatisUtils.getSqlSession(); 4 TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class); 5 Teacher teacher = mapper.getTeacher(1); 6 System.out.println(teacher); 7 sqlSession.close(); 8 } 9 10 @Test 11 public void test02(){ 12 SqlSession sqlSession = MybatisUtils.getSqlSession(); 13 StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); 14 for (Student student : mapper.getStudent()) { 15 System.out.println(student); 16 } 17 sqlSession.close(); 18 } 19 20 @Test 21 public void test03(){ 22 SqlSession sqlSession = MybatisUtils.getSqlSession(); 23 StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); 24 for (Student student : mapper.getStudent2()) { 25 System.out.println(student); 26 } 27 sqlSession.close(); 28 }
其中最重要的就是两种处理方式
方式一:按照查询嵌套处理
<!-- 思路: 1. 查询所有的学生信息 2. 根据查询出来的学生的tid寻找特定的老师 (子查询) --> <select id="getStudent" resultMap="StudentTeacher"> select * from student </select> <resultMap id="StudentTeacher" type="student"> <result property="id" column="id"/> <result property="name" column="name"/> <!--复杂的属性,我们需要单独出来 对象:association 集合:collection--> <collection property="teacher" column="tid" javaType="teacher" select="getTeacher"/> </resultMap> <select id="getTeacher" resultType="teacher"> select * from teacher where id = #{id} </select>
方式二:按照结果嵌套处理
1 <!--按照结果进行查询--> 2 <select id="getStudent2" resultMap="StudentTeacher2"> 3 select s.id sid , s.name sname, t.name tname 4 from student s,teacher t 5 where s.tid=t.id 6 </select> 7 <!--结果封装,将查询出来的列封装到对象属性中--> 8 <resultMap id="StudentTeacher2" type="student"> 9 <result property="id" column="sid"/> 10 <result property="name" column="sname"/> 11 <association property="teacher" javaType="teacher"> 12 <result property="name" column="tname"></result> 13 </association> 14 </resultMap>
回顾Mysql多对一查询方式:
- 子查询 (按照查询嵌套)
- 联表查询 (按照结果嵌套)
11、一对多处理
一个老师多个学生;
对于老师而言,就是一对多的关系;
1. 环境搭建
实体类
1 @Data 2 public class Student { 3 private int id; 4 private String name; 5 private int tid; 6 }
1 @Data 2 public class Teacher { 3 private int id; 4 private String name; 5 6 //一个老师拥有多个学生 7 private List<Student> students; 8 }
2. 按照结果嵌套嵌套处理(推荐使用的查询方式)
1 <!--按结果嵌套查询--> 2 <select id="getTeacher" resultMap="StudentTeacher"> 3 SELECT s.id sid, s.name sname,t.name tname,t.id tid FROM student s, teacher t 4 WHERE s.tid = t.id AND tid = #{tid} 5 </select> 6 <resultMap id="StudentTeacher" type="Teacher"> 7 <result property="id" column="tid"/> 8 <result property="name" column="tname"/> 9 <!--复杂的属性,我们需要单独处理 对象:association 集合:collection 10 javaType=""指定属性的类型! 11 集合中的泛型信息,我们使用ofType获取 12 --> 13 <collection property="students" ofType="Student"> 14 <result property="id" column="sid"/> 15 <result property="name" column="sname"/> 16 <result property="tid" column="tid"/> 17 </collection> 18 </resultMap>
子查询方式实现一对多
1 <select id="getTeacher2" resultMap="TeacherStudent2"> 2 select * from teacher where id=#{tid} 3 </select> 4 <resultMap id="TeacherStudent2" type="Teacher2"> 5 <collection property="student" javaType="ArrayList" ofType="Student2" select="getTeacherStudent2" column="id"/> 6 </resultMap> 7 <select id="getTeacherStudent2" resultType="Student2"> 8 select * 9 from student where tid = #{tid}; 10 </select>
小结
关联 - association 【多对一】
集合 - collection 【一对多】
javaType & ofType
JavaType用来指定实体类中的类型
ofType用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
注意点:
- 保证SQL的可读性,尽量保证通俗易懂
- 注意一对多和多对一,属性名和字段的问题
- 如果问题不好排查错误,可以使用日志,建议使用Log4j
面试高频
- Mysql引擎
- InnoDB底层原理
- 索引
- 索引优化
12、动态SQL
什么是动态SQL:动态SQL就是根据不同的条件生成不同的SQL语句
所谓的动态SQL,本质上还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
搭建环境
CREATE TABLE `mybatis`.`blog` ( `id` int(10) NOT NULL AUTO_INCREMENT COMMENT '博客id', `title` varchar(30) NOT NULL COMMENT '博客标题', `author` varchar(30) NOT NULL COMMENT '博客作者', `create_time` datetime(0) NOT NULL COMMENT '创建时间', `views` int(30) NOT NULL COMMENT '浏览量', PRIMARY KEY (`id`) )
创建一个基础工程
导包
编写配置文件
编写实体类
1 @Data 2 public class Blog { 3 private int id; 4 private String title; 5 private String author; 6 7 private Date createTime;// 属性名和字段名不一致 8 private int views; 9 }
BlogMapper
1 package com.bupt.dao; 2 3 import com.bupt.pojo.Blog; 4 5 import java.util.List; 6 import java.util.Map; 7 8 public interface BlogMapper { 9 10 int addBook(Blog blog); 11 12 List<Blog> queryBlogIf(Map map); 13 14 List<Blog> queryChoose(Map map); 15 16 //更新博客 17 int updateBlog(Map map); 18 }
BlogMapper.xml
通过使用动态sql可以动态的编写sql语句,并且动态sql可以自动去除多余的and和where和,等多余的内容。
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <mapper namespace="com.bupt.dao.BlogMapper"> 6 7 <insert id="addBook" parameterType="blog"> 8 insert into blog(id,title,author,create_time,views) values (#{id},#{title},#{author},#{createTime},#{views}); 9 </insert> 10 11 <select id="queryBlogIf" parameterType="map" resultType="blog"> 12 select * from blog 13 <where> 14 <if test="title != null"> 15 title = #{title} 16 </if> 17 <if test="author != null"> 18 and author = #{author} 19 </if> 20 </where> 21 </select> 22 23 <select id="queryChoose" parameterType="map" resultType="blog"> 24 select * from blog 25 <where> 26 <choose> 27 <when test="title != null"> 28 title = #{title} 29 </when> 30 <when test="author !=null"> 31 and author = #{author} 32 </when> 33 <otherwise> 34 and view = #{views} 35 </otherwise> 36 </choose> 37 </where> 38 </select> 39 40 <update id="updateBlog" parameterType="map"> 41 update blog 42 <set> 43 <if test="title != null"> 44 title = #{title}, 45 </if> 46 <if test="author != null"> 47 author = #{author} 48 </if> 49 </set> 50 where id = #{id} 51 </update> 52 53 54 </mapper>
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 blog <where> <include refid="if-title-author"></include> </where> </select>
13、缓存
13.1 简介
查询 : 连接数据库,耗资源 一次查询的结果,给他暂存一个可以直接取到的地方 --> 内存:缓存
我们再次查询的相同数据的时候,直接走缓存,不走数据库了
什么是缓存[Cache]?
- 存在内存中的临时数据。
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题
为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率
什么样的数据可以使用缓存?
- 经常查询并且不经常改变的数据 【可以使用缓存】
13.2 MyBatis缓存
- MyBatis包含一个非常强大的查询缓存特性,它可以非常方便的定制和配置缓存,缓存可以极大的提高查询效率。
- MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
- 为了提高可扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来定义二级缓存。
13.3 一级缓存
一级缓存也叫本地缓存:SqlSession
与数据库同一次会话期间查询到的数据会放在本地缓存中
以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
测试步骤:
开启日志
测试在一个Session中查询两次记录
@Test public void test1() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); System.out.println("====================================="); User user2 = mapper.getUserById(1); System.out.println(user2 == user); }
发现只进行了一次数据库连接
缓存失效的情况:
-
查询不同的东西
-
增删改操作,可能会改变原来的数据,所以必定会刷新缓存
-
查询不同的Mapper.xml
-
手动清理缓存
sqlSession.clearCache();
13.4 二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
- 工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
- 如果会话关闭了,这个会员对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中
- 新的会话查询信息,就可以从二级缓存中获取内容
- 不同的mapper查询出的数据会放在自己对应的缓存(map)中
- 一级缓存开启(SqlSession级别的缓存,也称为本地缓存)
二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
为了提高可扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来定义二级缓存。
步骤:
开启全局缓存
<!--显示的开启全局缓存,默认也是开启的,所以可以不写,但是写上其实是起到提醒的作用-->
<setting name="cacheEnabled" value="true"/>
在Mapper.xml中使用缓存
<!--在当前Mapper.xml中使用二级缓存-->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
-
测试
- 问题:我们需要将实体类序列化,否则就会报错
小结:
- 只要开启了二级缓存,在同一个Mapper下就有效
- 所有的数据都会放在一级缓存中
- 只有当前会话提交,或者关闭的时候,才会提交到二级缓存中
13.5 缓存原理
注意:
只有查询才有缓存,根据数据是否需要缓存(修改是否频繁选择是否开启)useCache=“true”
<select id="getUserById" resultType="user" useCache="true"> select * from user where id = #{id} </select>
13.6 自定义缓存-ehcache
Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存
导包
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
在mapper中指定使用我们的ehcache缓存实现
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>