MyBatis简介
MyBatis是一款优秀的持久层框架,用于简化JDBC开发。
MyBatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql中的动态参数进行映射生成最终执行的SQL语句。
最后MyBatis框架执行SQL并将结果集映射为java对象并返回。采用ORM思想(对象关系映射)解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。
持久层
- 负责将数据保存到数据库的那一层代码
- JavaEE三层架构:表现层(页面展示)、业务层(逻辑处理)、持久层(数据持久化)。
框架
- 框架就是一个半成品的软件,是一套可重用的、通用的、软件基础代码模型
- 在框架的基础之上构建软件编写更加高效、规范、通用、可拓展
JDBC缺点
- 硬编码(将一些信息写死到java代码里面了,比如数据库密码,查询语句等)
- 注册驱动,获取连接时
- SQL语句
- 操作繁琐
- 手动设置参数
- 手动封装结果集
应对上述问题给出的解决方案
- 使用数据库连接池初始化连接资源
- 将sql语句抽取到xml配置文件中
- 使用反射、内省等底层技术,自动将实体与表进行属性与字段的自动映射
MyBatis简化
MyBatis快速入门
- 添加MyBatis的坐标
- 创建user数据表
- 编写User实体类
- 编写映射文件UserMapper.xml(写SQL语句)
- 编写核心文件mybatis-config.xml(MyBatis核心配置,比如数据源这些)
- 编写测试类
- 加载mybatis的核心配置文件,获取 SqlSessionFactory
- 获取SqlSession对象,用它来执行sql
- 获取UserMapper接口的代理对象
- 执行方法
- 释放资源
实例:查询user表中的所有数据
-
创建user表,添加数据
create database mybatis; use mybatis; drop table if exists tb_user; create table tb_user( id int primary key auto_increment, username varchar(20), password varchar(20), gender char(1), addr varchar(30) ); INSERT INTO tb_user VALUES (1, 'zhangsan', '123', '男', '北京'); INSERT INTO tb_user VALUES (2, '李四', '234', '女', '天津'); INSERT INTO tb_user VALUES (3, '王五', '11', '男', '西安');
-
创建模块mybatis-demo,导入坐标
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>mybatis-demo</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <!-- mybatis依赖 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version> </dependency> <!-- Junit单元测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> <!-- slf4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.20</version> </dependency> <!-- 添加logback-classic依赖 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <!-- 添加logback-core依赖 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</version> </dependency> </dependencies> </project> </project>
-
编写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> <typeAliases> <package name="com.itheima.pojo"/> </typeAliases> <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:///mybatis?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> <environment id="test"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="1234"/> </dataSource> </environment> </environments> <mappers> <!--加载sql映射文件--> <!-- <mapper resource="com/itheima/mapper/UserMapper.xml"/>--> <!--Mapper代理包扫描方式--> <package name="com.itheima.mapper"/> </mappers> </configuration>
-
编写SQL映射文件 → 统一管理SQL语句,解决硬编码问题
SQL映射文件UserMapper.xml
<?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 namespace="com.itheima.mapper.UserMapper"> <select id="selectAll" resultType="user"> select * from tb_user; </select> <select id="selectById" resultType="user"> select * from tb_user where id = #{id}; </select> </mapper>
-
编码
-
定义POJO类
public class User { private Integer id; private String username; private String password; private String gender; private String addr; public Integer getId() { return id; } public void setId(Integer 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; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public String getAddr() { return addr; } public void setAddr(String addr) { this.addr = addr; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", gender='" + gender + '\'' + ", addr='" + addr + '\'' + '}'; } }
-
加载核心配置文件,获取SQLSessionFactory对象
-
获取SQLSession对象,执行SQL语句
-
释放资源
import com.itheima.pojo.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.util.List; /** * Mybatis 快速入门代码 */ public class MyBatisDemo { public static void main(String[] args) throws IOException { //1. 加载mybatis的核心配置文件,获取 SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2. 获取SqlSession对象,用它来执行sql SqlSession sqlSession = sqlSessionFactory.openSession(); //3. 执行sql List<User> users = sqlSession.selectList("com.itheima.mapper.UserMapper.selectAll"); System.out.println(users); //4. 释放资源 sqlSession.close(); } }
-
解决SQL映射文件的警告提示
- 产生原因:idea和数据库没有建立连接,不识别表信息
- 解决方式:在idea中配置MySQL数据库连接
MyBatis代理开发
目的:解决原生方式中的硬编码,简化后期执行SQL
-
定义与SQL映射文件同名的mapper接口,并且将mapper接口和SQL映射文件放置在同一目录下。(在resource下创建包使用/来分割每层而不能使用.分割)
-
设置SQL映射文件的namespace属性为mapper接口全限定名,即com.itheima.mapper.UserMappe
-
在mapper接口中定义方法,方法名就是SQL映射文件中SQL语句的id,并保持参数类型和返回值类型一致
UserMapper.java
package com.itheima.mapper; import com.itheima.pojo.User; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; public interface UserMapper { // 方法名要与UserMapper.xml中的SQL语句的id一致 // 返回值类型与SQL语句中的resultType一致 List<User> selectAll(); User selectById(int id); }
-
编码
- 通过SqlSession的getMapper方法获取Mapper接口的代理对象
- 调用对应方法完成SQL的执行
注意:如果Mapper接口名称和SQL映射文件名称相同,并在同一目录下,则可以使用包扫描的方式简化SQL映射文件的加载。
package com.itheima;
import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* Mybatis 代理开发
*/
public class MyBatisDemo2 {
public static void main(String[] args) throws IOException {
//1. 加载mybatis的核心配置文件,获取 SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3 获取UserMapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAll();
System.out.println(users);
//4. 释放资源
sqlSession.close();
}
}
MyBatis核心配置文件
mybatis核心配置文件的顶层结构如下:
- configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境变量)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
注意:配置各个标签时,需要遵守前后顺序
<?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>
<!-- 类型别名 -->
<typeAliases>
<package name="com.itheima.pojo"/>
</typeAliases>
<!--
environments:
配置数据库连接环境信息。
可以配置多个environment,通过default属性切换不同的environment
-->
<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:///mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库连接信息-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--加载sql映射文件-->
<!-- <mapper resource="com/itheima/mapper/UserMapper.xml"/>-->
<!--Mapper代理包扫描方式-->
<package name="com.itheima.mapper"/>
</mappers>
</configuration>
typeHandlers标签
无论是MyBatis在预处理语句(PrepareStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成Java类型,下面是部分默认的类型处理器:
可以重写类型处理器或创建自己的类型处理器来处理不支持的或非标准的类型,具体做法为:实现TypeHandler接口,或继承BaseTypeHandler,然后可以选择性地将它映射到一个JDBC类型。
例如:一个Java中的Date数据类型,我们想将它存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成Java的Date,即Java的Date和数据库的varchar毫秒值之间转换。
开发步骤:
- 定义转换类继承BaseTypeHandler<T>
- 覆盖四个未实现的方法,其中setNonNullParameter为Java程序设置数据到数据库的回调方法,getNullableResult为查询时mysql的字符串类型转换成Java的Type类型的方法
- 在MyBatis核心配置文件中进行注册
- 测试转换是否正确
示例:自定义DateTypeHandler类型处理器
public class DateTypeHandler extends BaseTypeHandler<Date>{
// 将Java类型转换成数据库需要的类型
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType){
long time = date.getTime(); // 将date转为毫秒值
preparedStatement.setLong(i, time);
}
// 将数据库中的类型转换成Java类型
// String参数:要转换的字段的名称
// ResultSet:查询出的结果集
public Date getNullableResult(ResultSet resultSet, String s) throws SQLException{
// 获取结果中需要的数据(long)转换成Date类型并返回
long aLong = reaultSet.getLong(s);
Date date = new Date(aLong);
return date;
}
// 将数据库中的类型转换成Java类型
public Date getNullableResult(ResultSet resultSet, int i) throws SQLException{
// 参数i表示的是字段在结果集中的位置
long aLong = reaultSet.getLong(i);
Date date = new Date(aLong);
return date;
}
// 将数据库中的类型转换成Java类型
public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException{
long aLong = callableStatement.getLong(s);
Date date = new Date(aLong);
return date;
}
}
在mybatis-config.xml中注册类型处理器
<typeHandlers>
<typeHandler handler="com.itheima.handler.DateTypeHandler"></typeHandler>
</typeHandlers>
plugins标签
MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据:
开发步骤:
- 导入通用的PageHelper的坐标
- 在mybatis核心配置文件中配置PageHelper插件
- 测试分页数据获取
示例:
- 导入通用的PageHelper的坐标
<!--分页助手-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>paghelper</artifactId>
<version>3.7.5</version>
</dependency>
<!--分页解析器-->
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>
- 在核心配置文件mybatis-config.xml中配置分页助手插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"></property>
</plugin>
</plugins>
- 测试分页数据获取
package com.itheima;
import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class PageHelperTest {
public static void main(String[] args) throws IOException {
//1. 加载mybatis的核心配置文件,获取 SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象,用它来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//3 获取UserMapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 设置分页相关参数:当前页 + 每页显示的条数
PageHelper.startPage(2,3);
List<User> users = userMapper.selectAll();
for(User user : users){
System.out.println(user);
}
// 获得与分页相关参数
PageInfo<User> pageInfo = new PageInfo<User>(users);
System.out.println("当前页:" + pageInfo.getPageNum());
System.out.println("每页显示条数:" + pageInfo.getPageSize());
System.out.println("总条数:" + pageInfo.getTotal());
System.out.println("总页数:" + pageInfo.getPages());
System.out.println("上一页:" + pageInfo.getPrePage());
System.out.println("下一页:" + pageInfo.getNextPage());
System.out.println("是否是第一页:" + pageInfo.isFirstPage());
System.out.println("是否是最后一页:" + pageInfo.isLastPage());
//4. 释放资源
sqlSession.close();
}
}
配置文件完成增删改查
MyBatisX插件
- 是一款基于idea的快速开发插件,为效率而生
- 主要功能
- xml和接口方法相互跳转
- 根据接口方法生成statement
查询
查询所有数据
-
编写接口方法:Mapper接口
- 参数:无
- 结果:List<Brand>
List<Brand> selectAll();
-
编写SQL语句:SQL映射文件
<!-- 数据库表的字段名称 和 实体类的属性名称 不一样,则不能自动封装数据 解决方法: 1.起别名,如 brand_name as brandName 缺点:每次查询都要定义别名 解决方法:SQL片段 缺点:对不同的查询需求不灵活 2.resultMap: 1.定义<resultMap>标签 2.在<select>标签中,使用resultMap属性替换resultType属性 --> <!-- 最基本的写法 --> <select id="selectAll" resultType="brand"> select id, brand_name as brandName, company_name as companyName, ordered, description, status from tb_brand; </select> <!-- 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> <!-- resultMap id:唯一标识 type:映射的类型,支持别名 --> <realutMap id="brandResultMap" type="brand"> <!-- id:完成主键字段的映射 column:表的列名 property:实体类的属性名 result:完成一般字段的映射 column:表的列名 property:实体类的属性名 --> <result column="brand_name" property="brandName"/> <result column="company_name" property="companydName"/> </realutMap> <select id="selectAll" resultMap="brandResultMap"> select *from tb_brand; </select>
-
执行方法,测试
package com.itheima.test; import com.itheima.mapper.BrandMapper; import com.itheima.pojo.Brand; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.HashMap; import java.util.List; import java.util.Map; public class MyBatisTest { @Test public void testSelectAll() throws IOException { //1. 获取SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2. 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //3. 获取Mapper接口的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //4. 执行方法 List<Brand> brands = brandMapper.selectAll(); System.out.println(brands); //5. 释放资源 sqlSession.close(); } }
查看详情
-
编写接口方法:Mapper接口
- 参数:id
- 结果:Brand
Brand selectById(int id);
-
编写SQL语句:SQL映射文件
<!-- 参数占位符: 1. #{}:执行SQL语句时会将其替换为?,为了防止SQL注入 2. ${}:简单的SQL语句拼接,存在SQL注入问题 3. 使用时机: - 在参数传递时,都使用#{} - 表名或者列名不固定的情况下使用${},存在SQL注入问题 参数类型:parameterType可省略 特殊字符处理: 1. 转义字符:\特殊字符 2. CDATA区:<![CDATA{特殊字符写在这}]> --> <select id="selectById" parameterType="int" resultMap="brandResultMap"> select *from tb_brand where id = #{id}; </select>
-
执行方法,测试
package com.itheima.test; import com.itheima.mapper.BrandMapper; import com.itheima.pojo.Brand; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.HashMap; import java.util.List; import java.util.Map; public class MyBatisTest { @Test public void testSelectById() throws IOException { //接收参数 int id = 1; //1. 获取SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2. 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //3. 获取Mapper接口的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //4. 执行方法 Brand brand = brandMapper.selectById(id); System.out.println(brand); //5. 释放资源 sqlSession.close(); } }
条件查询(动态SQL)
-
多条件查询
-
编写接口方法
- 参数:所有查询条件
- 结果:List<Brand>
/* mybatis 提供了三种不同的参数接收方式: 1.散装参数 需要使用@Param("SQL参数占位符名称") 例如SQL参数占位符#{status}的名称就是status 2.实体类封装参数 可以直接将其封装为一个对象来传递,#{status}时会自动找到对象的get方法获取参数值。 因此参数占位符的名称要和实体类的属性名一致,否则无法找到对应的get方法 3.map集合 要保证map集合的键的名称和SQL语句中的参数占位符的名称保持一致 */ List<Brand> selectByCondition(@Param("status")int status, @param("companyName") String companyName, @param("brandName") String brandName); List<Brand> selectByCondition(Brand brand); List<Brand> selectByCondition(Map map);
-
编写SQL语句:SQL映射文件
<select id="selectByCondition" resultMap="brandResultMap"> select * from tb_brand where status = #{status} and company_name like #{companyName} and brand_naem like #{brandName}; </select>
以上这种写法在程序运行时存在bug,例如:如果第三个参数的值是null,那么用户将查询不出结果。因为按照之前的代码,查询的是三个参数条件都满足的记录,此时编译后的SQL语句是
select * form tb_brand where status = ? and company_name = ? and brand_name = ?
而SQL语句会随着用户的输入或者外部条件的变化而变化,我们称为动态SQL。
mybatis对动态SQL有很强大的支撑:
-
if:条件判断
-
属性:test = “逻辑表达式”
-
问题:如果第一个<if>标签里面的条件不满足,则会报错,因为编译后的SQL语句是
select * from tr_brand where and company_name = ?
**解决方式1:**在where后面写一个恒等式,比如1=1,则编译后的SQL语句为
select * from tr_brand where 1 = 1 and company_name = ?
解决方式2:<where>标签替换where关键字,将所有的<if>标签写到<where>标签里
-
-
choose(when, otherwise)
-
trim(where, set)
-
foreach:循环遍历
-
sql:sql片段抽取
<!-- 动态条件查询 当用户只输入了status和companyName的值,此时brandName的值为空 那么编译后的SQL语句应该是 select * form tb_brand where status = ? and company_name = ? 不应该存在and brand_name = ?这一项 --> <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_naem like #{brandName} </if> </select>
-
-
执行方法,测试
package com.itheima.test; import com.itheima.mapper.BrandMapper; import com.itheima.pojo.Brand; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.HashMap; import java.util.List; import java.util.Map; public class MyBatisTest { @Test public void testSelectByCondition() throws IOException { //接收参数 int status = 1; String companyName = "华为"; String brandName = "华为"; // 处理参数 companyName = "%" + companyName + "%"; brandName = "%" + brandName + "%"; //封装对象 /* Brand brand = new Brand(); brand.setStatus(status); brand.setCompanyName(companyName); brand.setBrandName(brandName);*/ Map map = new HashMap(); map.put("status" , status); map.put("companyName", companyName); map.put("brandName" , brandName); //1. 获取SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2. 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //3. 获取Mapper接口的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //4. 执行方法 //List<Brand> brands = brandMapper.selectByCondition(status, companyName, brandName); //List<Brand> brands = brandMapper.selectByCondition(brand); List<Brand> brands = brandMapper.selectByCondition(map); System.out.println(brands); //5. 释放资源 sqlSession.close(); } }
单条件动态查询
-
编写接口方法
-
参数:查询条件
-
结果:List<Brand>
List<Brand> selectByConditionSingle(Brand brand);
-
-
编写SQL语句:SQL映射文件
从多个条件中选择一个使用choose(when, otherwise):选择,类似于Java中的switch语句
<select id="selectByConditionSingle" resultMap="brandResultMap"> select * from tb_brand <where> <!--相当于switch--> <choose> <!--相当于case--> <when test="status != null"> status = #{status} </when> <!--相当于case--> <when test="companyName != null and companyName != '' "> company_name like #{companyName} </when> <!--相当于case--> <when test="brandName != null and brandName != ''"> brand_name like #{brandName} </when> </choose> </where> </select>
-
执行方法,测试
package com.itheima.test; import com.itheima.mapper.BrandMapper; import com.itheima.pojo.Brand; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.HashMap; import java.util.List; import java.util.Map; public class MyBatisTest { @Test public void testSelectByConditionSingle() throws IOException { //接收参数 int status = 1; String companyName = "华为"; String brandName = "华为"; // 处理参数 companyName = "%" + companyName + "%"; brandName = "%" + brandName + "%"; //封装对象 Brand brand = new Brand(); //brand.setStatus(status); brand.setCompanyName(companyName); //brand.setBrandName(brandName); //1. 获取SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2. 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //3. 获取Mapper接口的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //4. 执行方法 List<Brand> brands = brandMapper.selectByConditionSingle(brand); System.out.println(brands); //5. 释放资源 sqlSession.close(); } }
-
添加
-
编写接口方法:Mapper接口
- 参数:除了id外的所有数据
- 结果:void
void add(Brand brand);
-
编写SQL语句:SQL映射文件
<insert id="add"> insert into tb_brand(brnad_name, company_name, ordered, description, status) values(#{brandName}, #{companyName}, #{oedered}, #{description}, #{status}); </insert>
-
执行方法,测试
- MyBatis事务:
- openSession():默认开启事务,进行增删改操作后需要使用sqlSession.commit();手动提交事务
- openSession(true):可以设置为自动提交事务(关闭事务)
package com.itheima.test; import com.itheima.mapper.BrandMapper; import com.itheima.pojo.Brand; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.HashMap; import java.util.List; import java.util.Map; public class MyBatisTest { @Test public void testAdd() throws IOException { //接收参数 int status = 1; String companyName = "波导手机"; String brandName = "波导"; String description = "手机中的战斗机"; int ordered = 100; //封装对象 Brand brand = new Brand(); brand.setStatus(status); brand.setCompanyName(companyName); brand.setBrandName(brandName); brand.setDescription(description); brand.setOrdered(ordered); //1. 获取SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2. 获取SqlSession对象,openSession是默认开始事务的,即需要手动提交事务 SqlSession sqlSession = sqlSessionFactory.openSession(); //设置openSession()的参数为true表示自动提交事务,即关闭事务 //SqlSession sqlSession = sqlSessionFactory.openSession(true); //3. 获取Mapper接口的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //4. 执行方法 brandMapper.add(brand); //提交事务 sqlSession.commit(); //5. 释放资源 sqlSession.close(); } }
- MyBatis事务:
添加——主键返回
在数据添加成功后,需要获取插入数据库数据的主键。比如:
- 添加订单和订单项
- 添加订单
- 添加订单项,订单项中需要设置所属订单的id
<!--
设置主键返回参数
userGenerateKeys = "true"
keyProperty = "id"
设置了这两个属性之后,可以在添加数据后直接获取到刚才添加对象在数据库生成的id值
-->
<insert id="addOrder" useGenerateKeys="true" keyProperty="id">
insert into tb_order(payment, payment_type, status) values(#{payment}, #{paymentType}, #{status});
</insert>
<insert id="addOrderItem">
insert into tb_order_item(goods_name, goods_price, price_count, order_id) values(#{goodsName}, #{goodsPrice}, #{count}, #{orderId});
</insert>
package com.itheima.test;
import com.itheima.mapper.BrandMapper;
import com.itheima.pojo.Brand;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyBatisTest {
@Test
public void testAdd2() throws IOException {
//接收参数
int status = 1;
String companyName = "波导手机";
String brandName = "波导";
String description = "手机中的战斗机";
int ordered = 100;
//封装对象
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
brand.setDescription(description);
brand.setOrdered(ordered);
//1. 获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//SqlSession sqlSession = sqlSessionFactory.openSession(true);
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
brandMapper.add(brand);
// 获取brand插入数据库后自动生成的id
Integer id = brand.getId();
System.out.println(id);
//提交事务
sqlSession.commit();
//5. 释放资源
sqlSession.close();
}
}
修改
修改全部字段
-
编写接口方法:Mapper接口
- 参数:所有数据
- 结果:void
void update(Brand brand);
-
编写SQL语句:SQL映射文件
<update id="update"> update tb_brand set brand_name = #{brandName}, company_name = #{companyName}, ordered = #{ordered}, description = #{description}, status = #{status} where id = #{id}; </update>
-
执行方法,测试
package com.itheima.test; import com.itheima.mapper.BrandMapper; import com.itheima.pojo.Brand; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.HashMap; import java.util.List; import java.util.Map; public class MyBatisTest { @Test public void testUpdate() throws IOException { //接收参数 int status = 0; String companyName = "波导手机"; String brandName = "波导"; String description = "波导手机,手机中的战斗机"; int ordered = 200; int id = 6; //封装对象 Brand brand = new Brand(); brand.setStatus(status); // brand.setCompanyName(companyName); // brand.setBrandName(brandName); // brand.setDescription(description); // brand.setOrdered(ordered); brand.setId(id); //1. 获取SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2. 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //SqlSession sqlSession = sqlSessionFactory.openSession(true); //3. 获取Mapper接口的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //4. 执行方法 int count = brandMapper.update(brand); System.out.println(count); //提交事务 sqlSession.commit(); //5. 释放资源 sqlSession.close(); } }
修改动态字段
-
编写接口方法:Mapper接口
- 参数:部分数据,封装到对象中
- 结果:void
-
编写SQL语句:SQL映射文件
<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>
-
执行方法,测试
删除
删除一个
-
编写接口方法:Mapper接口
- 参数:id
- 结果:void
void deleteById(int id);
-
编写SQL语句:SQL映射文件
<delete id="deleteById"> delete from tb_brand where id = #{id}; </delete>
-
执行方法,测试
package com.itheima.test; import com.itheima.mapper.BrandMapper; import com.itheima.pojo.Brand; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.HashMap; import java.util.List; import java.util.Map; public class MyBatisTest { @Test public void testDeleteById() throws IOException { //接收参数 int id = 6; //1. 获取SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2. 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //SqlSession sqlSession = sqlSessionFactory.openSession(true); //3. 获取Mapper接口的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //4. 执行方法 brandMapper.deleteById(id); //提交事务 sqlSession.commit(); //5. 释放资源 sqlSession.close(); } }
批量删除(foreach)
-
编写接口方法:Mapper接口
- 参数:id数组
- 结果:void
void deleteByIds(@Param(ids)int[] ids);
-
编写SQL语句:SQL映射文件
<delete id="deleteByIds"> delete from tb_brand where id in (?,?,?) </delete> <!-- 这里id的个数是动态变化的 --> <!-- mybatis会将数组参数封装为一个Map集合。 - 默认:key:array = value:数组,即collection的值默认要写array - 也可以使用@Param注解改变map集合的默认key的名称,此时collection的值就可以写ids - separator = ","表示遍历的每个元素以,为分隔符 - open="(" close=")"表示在开始和结束时在SQL语句中拼接上括号 --> <delete id="deleteByIds"> delete from tb_brand where id in <foreach collection="ids" item="id" separator="," open="(" close=")"> #{id} </foreach> ; </delete>
-
执行方法,测试
package com.itheima.test; import com.itheima.mapper.BrandMapper; import com.itheima.pojo.Brand; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.HashMap; import java.util.List; import java.util.Map; public class MyBatisTest { @Test public void testDeleteByIds() throws IOException { //接收参数 int[] ids = {5,7,8}; //1. 获取SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2. 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //SqlSession sqlSession = sqlSessionFactory.openSession(true); //3. 获取Mapper接口的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //4. 执行方法 brandMapper.deleteByIds(ids); //提交事务 sqlSession.commit(); //5. 释放资源 sqlSession.close(); } }
MyBatis参数传递
MyBatis接口方法中可以接受各种各样的参数,MyBatis底层对于这些参数进行不同的封装处理方式。MyBatis提供了 ParamNameResolver 类来进行参数封装。
单个参数
-
POJO类型:直接使用,属性名 和 参数占位符 一致
-
Map集合:直接使用,键名 和 参数占位符 一致
-
Collection:封装为Map集合,键名为 “arg0” 、“connection“
map.put(“arg0”, collection集合);
map.put(“conllection”, collection集合);
-
List:封装为Map集合,键名为 “arg0” 、“connection”和“list”
map.put(“arg0”, list集合);
map.put(“conllection”, list集合);
map.put(“list”, list集合);
-
Array:封装为Map集合,键名为 “arg0” 和 “array”
map.put(“arg0”, 数组);
map.put(“array”, 数组);
-
其他类型:比如int类型,直接使用,参数占位符名称叫什么都无所谓
注意:默认我们都使用@Param注解,替换Map集合中默认的arg键名(只会修改arg为自定义,不会修改其他键名)
多个参数
对于多个参数传递,MyBatis会将其封装成Map集合,默认方式为
map.put("arg0",参数值1);
map.put("param1",参数值1);
map.put("arg1",参数值2);
map.put("param2",参数值2);
如果在Mapper接口方法传递参数时,没有使用@Param注解定义键的名称,则对应的SQL语句参数占位符名称只能写”arg0“,”arg1“或者”param1“,”param2“。但是不推荐使用这种方式,代码的可读性太差。
// 使用@Param注解来定义参数名称,该名称要和SQL语句中的参数占位符的名称一致。
User select(@Param("username")String username, @Param("password")String password);
若使用了@Param(“username”)注解定义键的名称,则会将”arg0“替换成自定义的参数名,此时SQL语句参数占位符名称可以使用”username“或者”param1“。
注解完成增删改查
使用注解开发会比配置文件开发更加方便
提示:注解完成简单功能,配置文件完成复杂功能
@Select("select *from tb_user where id = #{id}")
public User selectById(int id);
- @Select
- @Insert
- @Update
- @Delete
- @Result:实现结果集封装
- @Results:可以与@Result一起使用,封装多个结果集
- @One:实现一对一结果集封装
- @Many:实现一对多结果集封装
示例:
MyBatis相应API
SqlSession工厂构建器SqlSessionFactoryBuilder
常用API:SqlSessionFactory build(InputStream inputStream)
通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象
String resource = "org/mybatis/builder/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);
其中,Resource工具类,这个类在org.apache.ibatis.io包中。Resource类帮助你从类路径下、文件系统或一个web URL中加载资源文件。
SqlSessionFactory有多个方法创建SqlSession实例,常用的有如下两个:
SqlSession会话对象
SqlSession实例在MyBatis中是一个非常强大的类,在这里你会看到所有执行语句、提交或回滚事务和获取映射器实例的方法。执行语句的方法主要有:
<T> T selectOne(String statement, Object parameter);
<E> List<E> selectList(String statement, Object parameter);
int insert(String statement, Object parameter);
int update(String statement, Object parameter);
int delete(String statement, Object parameter);
操作事务的方法主要有:
void commit();
void rollback();
MyBatis的Dao层具体实现
传统实现方式
手动对Dao进行实现
代理开发方式
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由MyBatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类的方法。
Mapper接口开发需要遵循以下规范:
- Mapper.xml文件中的namespace与mapper接口的全限定名相同
- Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
- Mapper接口方法的输入参数类型和Mapper.xml中定义的每个sql的parameterType的类型相同
- Mapper接口方法的输出参数类型和Mapper.xml中定义的每个sql的resultType的类型相同
// 代理方式对Dao进行实现
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
MyBatis多表查询
<resultMap id="orderMap" type="order">
<!--手动指定字段与实体属性的映射关系
column:数据库的字段名称
property:实体的属性名称
-->
<id column="oid" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
<!--
<result column="uid" property="user.id"></result>
<result column="username" property="user.username"></result>
<result column="password" property="user.password"></result>
<result column="birthday" property="user.birthday"></result>
-->
<!--
property:当前实体(order)中的属性名称(private User user)
javaType:当前实体(order)中的属性类型(User)
-->
<association property="user" javaType="User">
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
</association>
</resultMap>
<!--一对一:一个订单一个用户-->
<select id="findAll" resultMap="orderMap">
select *,o.id oid from orders o,user u where o.uid = u.id;
</select>
<resultMap id="userMap" type="order">
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
<!--配置集合信息
property:集合名称
ofType:当前集合中的数据类型
-->
<collection property="orderList" ofType="Order">
<!--封装order的数据-->
<id column="oid" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
</collection>
</resultMap>
<!--一对多:一个用户多个订单-->
<select id="findAll" resultMap="userMap">
select *,o.id oid from user u, orders o where u.id=o.id;
</select>
<resultMap id="userRoleMap" type="order">
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
<collection property="roleList" ofType="Role">
<id column="role_id" property="id"></id>
<result column="role_name" property="roleName"></result>
<result column="role_desc" property="roleDesc"></result>
</collection>
</resultMap>
<!--多对多:一个用户多个角色,一个角色被多个用户使用-->
<select id="findUserAndRoleAll" resultMap="userRoleMap">
select * from user u, sys_user_role ur, sys_role r where u.id = ur.user_id and ur.role_id = r.id;
</select>