MyBatis
MyBatis 是一款优秀的持久层框架,用于简化 JDBC 开发
官网:https://mybatis.org/mybatis-3/zh/index.html
持久层
负责将数据到保存到数据库的那一层代码
JavaEE三层架构:表现层、业务层、持久层
框架
框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型
在框架的基础之上构建软件编写更加高效、规范、通用、可扩展
MyBatis 快速入门
查询user表中所有数据
1.创建user表,添加数据
2.创建模块,导入坐标
3.编写 MyBatis 核心配置文件 -- > 替换连接信息 解决硬编码问题
4.编写 SQL 映射文件 --> 统一管理sql语句,解决硬编码问题
5.编码
1.定义POJO类
2.加载核心配置文件,获取 SqlSessionFactory 对象
3.获取 SqlSession 对象,执行 SQL 语句
4.释放资源
2.创建模块,导入坐标
要使用 MyBatis,只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可。
如果使用 Maven 来构建项目,则需将依赖代码置于 pom.xml 文件中。
pom.xml:
1 <?xml version="1.0" encoding="UTF-8"?> 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-demo</artifactId> 9 <version>1.0-SNAPSHOT</version> 10 11 <properties> 12 <maven.compiler.source>8</maven.compiler.source> 13 <maven.compiler.target>8</maven.compiler.target> 14 </properties> 15 16 <dependencies> 17 <!--mybatis 依赖--> 18 <dependency> 19 <groupId>org.mybatis</groupId> 20 <artifactId>mybatis</artifactId> 21 <version>3.5.5</version> 22 </dependency> 23 <!--mysql 驱动--> 24 <dependency> 25 <groupId>mysql</groupId> 26 <artifactId>mysql-connector-java</artifactId> 27 <version>5.1.46</version> 28 </dependency> 29 <!--junit 单元测试--> 30 <dependency> 31 <groupId>junit</groupId> 32 <artifactId>junit</artifactId> 33 <version>4.13</version> 34 <scope>test</scope> 35 </dependency> 36 <!-- 添加slf4j日志api --> 37 <dependency> 38 <groupId>org.slf4j</groupId> 39 <artifactId>slf4j-api</artifactId> 40 <version>1.7.20</version> 41 </dependency> 42 <!-- 添加logback-classic依赖 --> 43 <dependency> 44 <groupId>ch.qos.logback</groupId> 45 <artifactId>logback-classic</artifactId> 46 <version>1.2.3</version> 47 </dependency> 48 <!-- 添加logback-core依赖 --> 49 <dependency> 50 <groupId>ch.qos.logback</groupId> 51 <artifactId>logback-core</artifactId> 52 <version>1.2.3</version> 53 </dependency> 54 </dependencies> 55 </project>
3.编写 MyBatis 核心配置文件 -- > 替换连接信息 解决硬编码问题
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 7 <typeAliases> 8 <package name="com.test.pojo"/> 9 </typeAliases> 10 11 <!-- 12 environments:配置数据库连接环境信息。可以配置多个environment,通过default属性切换不同的environment 13 --> 14 <environments default="development"> 15 <environment id="development"> 16 <transactionManager type="JDBC"/> 17 <dataSource type="POOLED"> 18 <!--数据库连接信息--> 19 <property name="driver" value="com.mysql.jdbc.Driver"/> 20 <property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/> 21 <property name="username" value="root"/> 22 <property name="password" value="1234"/> 23 </dataSource> 24 </environment> 25 </environments> 26 <mappers> 27 <!--加载sql映射文件--> 28 <mapper resource="UserMapper.xml"/> 29 </mappers> 30 </configuration>
4.编写 SQL 映射文件 --> 统一管理sql语句,解决硬编码问题
UserMapper.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 6 <!-- 7 namespace:名称空间 8 --> 9 10 <mapper namespace="test"> 11 <!--statement--> 12 <select id="selectAll" resultType="com.test.pojo.User"> 13 select * from tb_user; 14 </select> 15 </mapper>
5.编码
1.定义POJO类
User.java:
1 public class User { 2 3 private Integer id; 4 private String username; 5 private String password; 6 private String gender; 7 private String addr; 8 9 public Integer getId() { 10 return id; 11 } 12 public void setId(Integer id) { 13 this.id = id; 14 } 15 ... 16 @Override 17 public String toString() { 18 ... 19 } 20 }
2.加载核心配置文件,获取 SqlSessionFactory 对象
3.获取 SqlSession 对象,执行 SQL 语句
4.释放资源
MyBatisDemo.java:
1 public static void main(String[] args) throws IOException { 2 //1. 加载mybatis的核心配置文件,获取 SqlSessionFactory 3 String resource = "mybatis-config.xml"; 4 InputStream inputStream = Resources.getResourceAsStream(resource); 5 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 6 7 //2. 获取SqlSession对象,用它来执行sql 8 SqlSession sqlSession = sqlSessionFactory.openSession(); 9 //3. 执行sql 10 List<User> users = sqlSession.selectList("test.selectAll"); 11 System.out.println(users); 12 //4. 释放资源 13 sqlSession.close(); 14 }
解决SQL映射文件的警告提示
UserMapper.xml存在警告信息:
产生原因:Idea和数据库没有建立连接,不识别表信息
解决方式:在Idea中配置MySQL数据库连接
Mapper 代理开发
目的:
解决原生方式中的硬编码
简化后期执行SQL
使用 Mapper 代理方式完成入门案例
1、定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下
创建mybatis-demo/src/main/java/com/test/mapper/UserMapper.java
创建mybatis-demo/src/main/resources/com/test/mapper,将UserMapper.xml放到该文件夹下。(注:resources文件夹下需要使用【com/test/mapper】方式来定义文件夹,不能使用【com.test.mapper】来定义文件夹)
2、设置SQL映射文件的namespace属性为Mapper接口全限定名
修改UserMapper.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 6 <!-- 7 namespace:名称空间 8 --> 9 10 <!--<mapper namespace="test">--> 11 <mapper namespace="com.test.mapper.UserMapper"> 12 <!--statement--> 13 <select id="selectAll" resultType="com.test.pojo.User"> 14 select * from tb_user; 15 </select> 16 </mapper>
3、在 Mapper 接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致
UserMapper.java:
1 public interface UserMapper { 2 List<User> selectAll(); 3 }
注:
1、UserMapper.xml位置变更,MyBatis核心配置文件mybatis-config.xml中加载sql映射文件路径也需修改;
2、如果Mapper接口名称和SQL映射文件名称相同,并在同一目录下,则可以使用包扫描的方式简化SQL映射文件的加载
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 7 <typeAliases> 8 <package name="com.test.pojo"/> 9 </typeAliases> 10 11 <!-- 12 environments:配置数据库连接环境信息。可以配置多个environment,通过default属性切换不同的environment 13 --> 14 <environments default="development"> 15 <environment id="development"> 16 <transactionManager type="JDBC"/> 17 <dataSource type="POOLED"> 18 <!--数据库连接信息--> 19 <property name="driver" value="com.mysql.jdbc.Driver"/> 20 <property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/> 21 <property name="username" value="root"/> 22 <property name="password" value="1234"/> 23 </dataSource> 24 </environment> 25 </environments> 26 <mappers> 27 <!--加载sql映射文件--> 28 <!--<mapper resource="UserMapper.xml"/>--> 29 <!--修改1--> 30 <!--<mapper resource="com/test/mapper/UserMapper.xml"/>--> 31 <!--修改2--> 32 <!--Mapper代理方式--> 33 <package name="com.test.mapper"/> 34 </mappers> 35 </configuration>
4、编码
通过 SqlSession 的 getMapper方法获取 Mapper接口的代理对象
调用对应方法完成sql的执行
MyBatisDemo.java:
1 public static void main(String[] args) throws IOException { 2 //1. 加载mybatis的核心配置文件,获取 SqlSessionFactory 3 String resource = "mybatis-config.xml"; 4 InputStream inputStream = Resources.getResourceAsStream(resource); 5 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 6 7 //2. 获取SqlSession对象,用它来执行sql 8 SqlSession sqlSession = sqlSessionFactory.openSession(); 9 //3. 执行sql 10 //List<User> users = sqlSession.selectList("test.selectAll"); 11 //3.1 获取UserMapper接口的代理对象 12 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 13 List<User> users = userMapper.selectAll(); 14 System.out.println(users); 15 //4. 释放资源 16 sqlSession.close(); 17 }
MyBatis 核心配置文件详解
MyBatis 核心配置文件的顶层结构如下:(注:配置各个标签时,需要遵守前后顺序)
类型别名(typeAliases)
在mybatis-config.xml中添加类型别名(typeAliases)
1 <typeAliases > 2 <package name="com.test.pojo"/> 3 </typeAliases>
在UserMapper.xml中写返回类型时可直接写类名(不区分大小写)
UserMapper.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 6 <!-- 7 namespace:名称空间 8 --> 9 <mapper namespace="com.test.mapper.UserMapper"> 10 <!--<select id="selectAll" resultType="com.test.pojo.User">--> 11 <select id="selectAll" resultType="user"> 12 select * from tb_user; 13 </select> 14 </mapper>
Mybatis缓存
缓存就是内存中的数据,常常来自对数据库查询结果的保存,使用缓存可以避免频繁与数据库进行交互,从而提高查询响应速度。
MyBatis 提供了对缓存的支持,分为一级缓存和二级缓存。
一级缓存: SqlSession级别的缓存,缓存的数据只在SqlSession内有效。
二级缓存: mapper级别的缓存,同一个namespace公用这一个缓存,所以对SqlSession是共享的,二级缓存需要我们手动开启。
Mybatis一级缓存
1.为什么需要Mybatis一级缓存
当我们使用Mybatis进行数据库的操作时候,会创建一个SqlSession来进行一次数据库的会话,会话结束则关闭SqlSession对象。如果我们很有可能多次查询完全相同的sql语句,每一次查询都查询一次数据库,那查询数据库代价是比较大的,这会导致系统的资源浪费。
为了解决这个问题,Mybatis对每一次会话都添加了缓存操作,不需要相同的SQL每次都查询数据库,这就是Mybatis一级缓存的作用。
2.Mybatis一级缓存的实现
我们知道对SqlSession的操作,mybatis内部都是通过Executor来执行的,Executor的生命周期和SqlSession是一致的,Mybatis在Executor中创建了本地缓存(一级缓存)
如果sqlSession执行close、commit、插入、更新、删除操作,清空sqlSession中的一级缓存,保证缓存中始终保存的是最新的信息,避免脏读。(当对SqlSession执行更新操作(update、delete、insert)后并执行commit时,不仅清空其自身的一级缓存(执行更新操作的效果),也清空二级缓存(执行commit()的效果))。
注:在mybatis与spring进行整合开发时,事务控制在service中进行,重复调用两次servcie将不会走一级缓存,因为在第二次调用时session方法结束,SqlSession就关闭了。
3.Mybatis一级缓存配置
mybatis一级缓存的范围有SESSION和STATEMENT两种,默认是SESSION。
如果不想使用一级缓存,可以把一级缓存的范围指定为STATEMENT,这样每次执行完一个Mapper中的语句后都会将一级缓存清除.如果需要更改一级缓存的范围,可以在Mybatis的配置文件中,在下通过localCacheScope指定:
<setting name="localCacheScope" value="STATEMENT"/>
Mybatis二级缓存
1.为什么需要Mybatis二级缓存?
MyBatis 一级缓存最大的共享范围就是一个SqlSession内部,那么如果多个 SqlSession 需要共享缓存,则需要开启二级缓存.
2.Mybatis二级缓存的实现
开启二级缓存后,会使用 CachingExecutor 装饰 Executor,进入一级缓存的查询流程前,先在 CachingExecutor 进行二级缓存的查询。
二级缓存开启后,一个 namespace 下的所有操作语句,都影响着同一个 Cache,即二级缓存被多 SqlSession 共享,是一个全局的变量。
当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库
MyBatis 是默认关闭二级缓存的,因为对于增删改操作频繁的话,那么二级缓存形同虚设,每次都会被清空缓存。
Mybatis一级缓存与二级缓存的区别
1.Mybatis的一级缓存的作用域是SQLSession,Mybatis默认开启一级缓存。
在同一个SqlSession中,执行相同的SQL查询时,第一次会去查询数据库,并写在缓存中,第二次会直接从缓存中取。当执行SQL时候两次查询中间发生了增删改的操作commit后,SQLSession的缓存会被清空。
每次查询会先去缓存中找,如果找不到,再去数据库查询,然后把结果写到缓存中。Mybatis的内部缓存使用一个HashMap,key为hashcode+statementld+sql语句。Value为查询出来的结果集映时成的java对象。
2.Mbatis二级缓存是默认不开启的,作用于一个Applicaton,是Mapper级别的,多个SqlSession使用同一个Mapper的sql能够使用二级缓存。
配置文件完成增删改查
MyBatisX 插件
MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。
主要功能:
XML 和 接口方法 相互跳转
根据接口方法生成 statement
安装:点击file->settings
实体类属性名和数据库表列名不一致导致封装数据失败问题
1、给字段起别名
1、在写sql语句时给这两个字段起别名,将别名定义成与属性名一致:
<select id="selectAll" resultType="brand"> select id, brand_name as brandName, company_name as companyName, ordered, description, status from tb_brand; </select>
上面的SQL语句中的字段列表书写麻烦,Mybatis提供了sql片段可以提高sql的复用性。
2、SQL片段(不灵活):
<sql id="brand_column"> id, brand_name as brandName, company_name as companyName, ordered, description, status </sql> <select id="selectAll" resultType="brand"> select <include refid="brand_column" /> from tb_brand; </select>
2、使用resultMap定义字段和属性的映射关系
BrandMapper.xml:
<resultMap id="brandResultMap" type="brand"> <!--id:唯一标识;type:需要映射的类型 --> <result column="brand_name" property="brandName"/> <result column="company_name" property="companyName"/> </resultMap> <select id="selectAll" resultMap="brandResultMap"> select * from tb_brand; </select>
id:完成主键字段的映射
column:表的列名
property:实体类的属性名
例:<id column="id" property="id1" />
result:完成一般字段的映射
column:表的列名
property:实体类的属性名
注意:只需定义字段名和属性名不一样的映射,一样的不需要定义。
用注解实现resultMap
BrandMapper.xml:
<resultMap id="brandResultMap" type="brand"> <result column="brand_name" property="brandName"></result> <result column="company_name" property="companyName"></result> </resultMap>
BrandMapper.java:
1 public interface BrandMapper { 2 @Select("select * from tb_brand") 3 @ResultMap("brandResultMap") 4 List<Brand> selectAll(); 5 }
<select id="selectById" resultMap="brandResultMap"> select * from tb_brand where id = #{id}; </select>
两种参数占位符
#{} :执行SQL时,会将#{}占位符替换为?,为了防止SQL注入。底层使用的是 PreparedStatement。
${} :拼接SQL。底层使用的是Statement,会存在SQL注入问题。
使用时机:
参数传递的时候:#{}
表名或者列名不固定的情况下:${}。但会存在SQL注入问题。
parameterType使用
对于有参数的mapper接口方法,我们在映射配置文件中应该配置ParameterType来指定参数类型。只不过该属性都可以省略。
<select id="selectById" parameterType="int" resultMap="brandResultMap"> select * from tb_brand where id = ${id}; </select>
映射配置文件是xml类型的问题,sql中 > < 等这些字符在xml中有特殊含义,所以需要将这些符号进行转义,可以使用以下两种方式进行转义
<select id="selectById" resultMap="brandResultMap"> select * from tb_brand where id < #{id}; </select>
1. 转义字符:
< 就是<的转义字符
<select id="selectById" resultMap="brandResultMap"> select * from tb_brand where id < #{id}; </select>
2. CDATA区:
<![CDATA[内容]]>
<select id="selectById" resultMap="brandResultMap"> select * from tb_brand where id <![CDATA[ < ]]> #{id}; </select>
多条件查询
编写接口方法
Mybatis针对多参数有三种实现:
1、散装参数(使用麻烦)
使用 @Param("参数名称") 标记每一个参数,在映射配置文件中就需要使用 #{参数名称} 进行占位
List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName,@Param("brandName") String brandName);
2、实体类封装参数
将多个参数封装成一个实体对象 ,将该实体对象作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内容} 时,里面的内容必须和实体类属性名保持一致。
List<Brand> selectByCondition(Brand brand);
3、map集合
将多个参数封装到map集合中,将map集合作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内容} 时,里面的内容必须和map集合中键的名称一致。
List<Brand> selectByCondition(Map map);
<select id="selectByCondition" resultMap="brandResultMap"> select * from tb_brand where status = #{status} and company_name like #{companyName} and brand_name like #{brandName} </select>
1 public void testSelectByCondition() throws IOException { 2 //接收参数 3 int status = 1; 4 String companyName = "华为"; 5 String brandName = "华为"; 6 7 // 处理参数 8 companyName = "%" + companyName + "%"; 9 brandName = "%" + brandName + "%"; 10 11 //1. 获取SqlSessionFactory 12 String resource = "mybatis-config.xml"; 13 InputStream inputStream = Resources.getResourceAsStream(resource); 14 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 15 //2. 获取SqlSession对象 16 SqlSession sqlSession = sqlSessionFactory.openSession(); 17 //3. 获取Mapper接口的代理对象 18 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); 19 20 //4. 执行方法 21 //方式一 :接口方法参数使用 @Param 方式调用的方法 22 //List<Brand> brands = brandMapper.selectByCondition(status, companyName, brandName); 23 //方式二 :接口方法参数是 实体类对象 方式调用的方法 24 //封装对象 25 /* Brand brand = new Brand(); 26 brand.setStatus(status); 27 brand.setCompanyName(companyName); 28 brand.setBrandName(brandName);*/ 29 //List<Brand> brands = brandMapper.selectByCondition(brand); 30 31 //方式三 :接口方法参数是 map集合对象 方式调用的方法 32 Map map = new HashMap(); 33 map.put("status" , status); 34 map.put("companyName", companyName); 35 map.put("brandName" , brandName); 36 List<Brand> brands = brandMapper.selectByCondition(map); 37 System.out.println(brands); 38 39 //5. 释放资源 40 sqlSession.close(); 41 }
动态SQL
SQL语句会随着用户的输入或外部条件的变化而变化
MyBatis 对动态SQL有很强大的支撑:
if
choose (when, otherwise)
trim (where, set)
foreach
if 标签
用于判断参数是否有值,使用test属性进行条件判断(test属性:逻辑表达式)
<select id="selectByCondition" resultMap="brandResultMap"> select * from tb_brand where <if test="status != null"> status = #{status} </if> <if test="companyName != null and companyName != '' "> and company_name like #{companyName} </if> <if test="brandName != null and brandName != '' "> and brand_name like #{brandName} </if> </select>
存在的问题:第一个参数不输入,sql语句会变为 select * from tb_brand where and company_name like ? and brand_name like ?
解决方案:
1、使用恒等式让所有条件格式都一样
<select id="selectByCondition" resultMap="brandResultMap"> select * from tb_brand where 1 = 1 <if test="status != null"> and status = #{status} </if> <if test="companyName != null and companyName != '' "> and company_name like #{companyName} </if> <if test="brandName != null and brandName != '' "> and brand_name like #{brandName} </if> </where> </select>
2、<where> 标签
作用:
替换where关键字
会动态的去掉第一个条件前的 and
如果所有的参数没有值则不加where关键字
<select id="selectByCondition" resultMap="brandResultMap"> select * from tb_brand <where> <if test="status != null"> and status = #{status} </if> <if test="companyName != null and companyName != '' "> and company_name like #{companyName} </if> <if test="brandName != null and brandName != '' "> and brand_name like #{brandName} </if> </where> </select>
单条件查询
从多个条件中选择一个
choose (when, otherwise):选择,类似于Java中的switch语句
<select id="selectByConditionSingle" resultMap="brandResultMap"> select * from tb_brand where <choose><!--相当于switch--> <when test="status != null"><!--相当于case--> status = #{status} </when> <when test="companyName != null and companyName != '' "><!--相当于case--> company_name like #{companyName} </when> <when test="brandName != null and brandName != ''"><!--相当于case--> brand_name like #{brandName} </when> <otherwise><!--相当于default--> 1 = 1 </otherwise> </choose> </select>
或结合<where>标签
<select id="selectByConditionSingle" resultMap="brandResultMap"> select * from tb_brand <where> <choose><!--相当于switch--> <when test="status != null"><!--相当于case--> status = #{status} </when> <when test="companyName != null and companyName != '' "><!--相当于case--> company_name like #{companyName} </when> <when test="brandName != null and brandName != ''"><!--相当于case--> brand_name like #{brandName} </when> </choose> </where> </select>
添加
编写接口方法
void add(Brand brand);
BrandMapper.xml:
<insert id="add" useGeneratedKeys="true" keyProperty="id"> insert into tb_brand (brand_name, company_name, ordered, description, status) values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status}); </insert>
MyBatis事务:
openSession():默认开启事务,进行增删改操作后需要使用 sqlSession.commit(),手动提交事务。
openSession(true):可以设置为自动提交事务
MybatisTest.java:
1 public void testAdd() throws IOException { 2 //接收参数 3 int status = 1; 4 String companyName = "手机"; 5 String brandName = "手机"; 6 String description = "手机"; 7 int ordered = 100; 8 9 //封装对象 10 Brand brand = new Brand(); 11 brand.setStatus(status); 12 brand.setCompanyName(companyName); 13 brand.setBrandName(brandName); 14 brand.setDescription(description); 15 brand.setOrdered(ordered); 16 17 //1. 获取SqlSessionFactory 18 String resource = "mybatis-config.xml"; 19 InputStream inputStream = Resources.getResourceAsStream(resource); 20 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 21 //2. 获取SqlSession对象 22 SqlSession sqlSession = sqlSessionFactory.openSession(); 23 //SqlSession sqlSession = sqlSessionFactory.openSession(true); //设置自动提交事务,这种情况不需要手动提交事务了 24 //3. 获取Mapper接口的代理对象 25 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); 26 //4. 执行方法 27 brandMapper.add(brand); 28 //提交事务 29 sqlSession.commit(); 30 //5. 释放资源 31 sqlSession.close(); 32 }
添加–主键返回
在数据添加成功后,需要获取插入数据库数据的主键
比如:添加订单和订单项,订单项中需要设置所属订单的id
在insert标签上添加如下属性:
useGeneratedKeys:获取自动增长的主键值。true表示获取。
keyProperty:指定将获取到的主键值封装到哪儿个属性里
BrandMapper.xml:
<insert id="add" useGeneratedKeys="true" keyProperty="id"> insert into tb_brand (brand_name, company_name, ordered, description, status) values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status}); </insert>
<insert id="insert" parameterType="Student"> INSERT INTO tb_student (name, age, phone, address, class_id) VALUES (#{name},#{age},#{phone},#{address},#{classId}) </insert>
mapper接口:
1 public interface StudentMapper{ 2 int insert(Student student); 3 }
测试代码:
1 @SpringBootTest 2 class DemoApplicationTests { 3 @Resource 4 private StudentMapper studentMapper; 5 6 @Test 7 public void testInsert(){ 8 //数据生成 9 List<Student> studentList = createData(100); 10 //循环插入 11 long start = System.currentTimeMillis(); 12 studentList.stream().forEach(student -> studentMapper.insert(student)); 13 System.out.println(System.currentTimeMillis() - start); 14 } 15 private List<Student> createData(int size){ 16 List<Student> studentList = new ArrayList<>(); 17 Student student; 18 for(int i = 0; i < size; i++){ 19 student = new Student(); 20 student.setName("小王" + i); 21 student.setAge(18); 22 student.setClassId(1); 23 student.setPhone("1585xxxx669"); 24 student.setAddress("未知"); 25 studentList.add(student); 26 } 27 return studentList; 28 } 29 }
<insert id="insertBatch" > INSERT INTO tb_student (name, age, phone, address, class_id) VALUES <foreach collection="list" separator="," item="item"> (#{item.name},#{item.age},#{item.phone},#{item.address},#{item.classId}) </foreach> </insert>
mapper接口:
1 public interface StudentMapper{ 2 int insertBatch(List<Student> studentList); 3 }
测试代码:
1 @SpringBootTest 2 class DemoApplicationTests { 3 @Resource 4 private StudentMapper studentMapper; 5 6 @Test 7 public void testInsert(){ 8 //数据生成 9 List<Student> studentList = createData(100); 10 //使用foreach标签拼接sql插入 11 long start = System.currentTimeMillis(); 12 studentMapper.insertBatch(studentList); 13 System.out.println(System.currentTimeMillis() - start); 14 } 15 private List<Student> createData(int size){ 16 List<Student> studentList = new ArrayList<>(); 17 Student student; 18 for(int i = 0; i < size; i++){ 19 student = new Student(); 20 student.setName("小王" + i); 21 student.setAge(18); 22 student.setClassId(1); 23 student.setPhone("1585xxxx669"); 24 student.setAddress("未知"); 25 studentList.add(student); 26 } 27 return studentList; 28 } 29 }
<insert id="insert" parameterType="Student"> INSERT INTO tb_student (name, age, phone, address, class_id) VALUES (#{name},#{age},#{phone},#{address},#{classId}) </insert>
mapper接口:
1 public interface StudentMapper{ 2 int insert(Student student); 3 }
测试代码:
1 @SpringBootTest 2 class DemoApplicationTests { 3 @Autowired 4 private SqlSessionFactory sqlSessionFactory; 5 6 @Test 7 public void testInsertBatch(){ 8 //数据生成 9 List<Student> studentList = createData(100); 10 //使用批处理 11 long start = System.currentTimeMillis(); 12 SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); 13 StudentMapper studentMapperNew = sqlSession.getMapper(StudentMapper.class); 14 //studentList.stream().forEach(student -> studentMapperNew.insert(student)); //实现一 15 for (Student student : studentList) { //实现二 16 studentMapperNew.insert(student); 17 } 18 sqlSession.commit(); 19 sqlSession.clearCache(); 20 System.out.println(System.currentTimeMillis() - start); 21 } 22 private List<Student> createData(int size){ 23 List<Student> studentList = new ArrayList<>(); 24 Student student; 25 for(int i = 0; i < size; i++){ 26 student = new Student(); 27 student.setName("小王" + i); 28 student.setAge(18); 29 student.setClassId(1); 30 student.setPhone("1585xxxx669"); 31 student.setAddress("未知"); 32 studentList.add(student); 33 } 34 return studentList; 35 } 36 }
三种方式中,批处理的方式效率是最高的,其次是foreach标签,最后是循环插入的方式。
修改全部字段
BrandMapper.xml:
<update id="update"> update tb_brand set brand_name = #{brandName}, company_name = #{companyName}, ordered = #{ordered}, description = #{description}, status = #{status} where id = #{id}; </update>
修改部分字段
BrandMapper.xml:
<update id="update"> update tb_brand <set> <if test="brandName != null and brandName != ''"> brand_name = #{brandName}, </if> <if test="companyName != null and companyName != ''"> company_name = #{companyName}, </if> <if test="ordered != null"> ordered = #{ordered}, </if> <if test="description != null and description != ''"> description = #{description}, </if> <if test="status != null"> status = #{status} </if> </set> where id = #{id}; </update>
删除
删除一个
BrandMapper.xml:
<delete id="deleteById"> delete from tb_brand where id = #{id}; </delete>
编写SQL时需要遍历数组来拼接SQL语句。Mybatis提供了foreach标签使用
foreach标签
用来迭代任何可迭代的对象(如数组,集合)。
collection 属性:
mybatis会将数组参数,封装为一个Map集合。
默认:1.array = 数组
2.使用@Param注解改变map集合的默认key的名称
item 属性:本次迭代获取到的元素。
separator 属性:集合项迭代之间的分隔符。foreach标签不会错误地添加多余的分隔符。也就是最后一次迭代不会加分隔符。
open 属性:该属性值是在拼接SQL语句之前拼接的语句,只会拼接一次
close 属性:该属性值是在拼接SQL语句拼接后拼接的语句,只会拼接一次
1.使用array
UserMapper.java:
void deleteByIds(int[] ids);
BrandMapper.xml:
<delete id="deleteByIds"> delete from tb_brand where id in <foreach collection="array" item="id" separator="," open="(" close=")"> #{id} </foreach> ; </delete>
2.使用@Param注解
void deleteByIds(@Param("ids") int[] ids);
BrandMapper.xml:
<delete id="deleteByIds"> delete from tb_brand where id in <foreach collection="ids" item="id" separator="," open="(" close=")"> #{id} </foreach> ; </delete>
Mybatis 接口方法中可以接收各种各样的参数,如下:
多个参数
单个参数:
POJO 类型
Map 集合类型
Collection 集合类型
List 集合类型
Array 类型
其他类型
多个参数
我们在接口方法中定义多个参数,Mybatis 会将这些参数封装成 Map 集合对象,值就是参数值,而键在没有使用 @Param 注解时有以下命名规则:
以 arg 开头 :第一个参数就叫 arg0,第二个参数就叫 arg1,以此类推。如:
map.put("arg0",参数值1);
map.put("arg1",参数值2);
以 param 开头 : 第一个参数就叫 param1,第二个参数就叫 param2,依次类推。如:
map.put("param1",参数值1);
map.put("param2",参数值2);
在接口方法参数上使用 @Param 注解,Mybatis 会将 arg 开头的键名替换为对应注解的属性值。
User select(@Param("username") String username,@Param("password") String password);
<select id="select" resultType="user"> select * from tb_user where username=#{username} and password=#{password} </select>
结论:以后接口参数是多个时,在每个参数上都使用 @Param 注解。这样代码的可读性更高。
单个参数
POJO 类型
直接使用。要求属性名和参数占位符名称一致
Map 集合类型
直接使用。要求map集合的键名和参数占位符名称一致
Collection 集合类型
Mybatis 会将集合封装到 map 集合中,如下:
map.put("arg0",collection集合);
map.put("collection",collection集合;
可以使用 @Param 注解替换map集合中默认的 arg 键名。
List 集合类型
Mybatis 会将集合封装到 map 集合中,如下:
map.put("arg0",list集合);
map.put("collection",list集合);
map.put("list",list集合);
可以使用 @Param 注解替换map集合中默认的 arg 键名。
Array 类型
Mybatis 会将集合封装到 map 集合中,如下:
map.put("arg0",数组);
map.put("array",数组);
可以使用 @Param 注解替换map集合中默认的 arg 键名。
其他类型
比如int类型,参数占位符名称叫什么都可以。尽量做到见名知意.
@Select(value = "select * from tb_user where id = #{id}") public User select(int id);
Mybatis 针对 CURD 操作都提供了对应的注解,已经做到见名知意。如下:
查询 :@Select
添加 :@Insert
修改 :@Update
删除 :@Delete
注:注解完成简单功能,配置文件完成复杂功能。