MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
简单来讲,Mybatis就是一个简化JDBC(对JDBC代码进行封装)的框架,有了Mybatis我们就不用编写繁琐的JDBC代码,大大提高了我们编写代码的效率
JavaEE三层框架:表现层、业务层、持久层
那么什么是持久层呢??
持久层指的是负责将数据保存到数据库中的那些关键代码,因此在以后的Java程序开发中,我们将操作数据库的Java代码作为持久层。
在前面已经提了一下JDBC的缺点,就是繁琐,我们每次对数据库进行操作都是要注册驱动然后获取链接,接下来再对编写sql语句,极度不便开发。
在Mybatis中,JDBC的缺点都得到了一定程度下的优化,减少了程序猿的工作压力
想要使用Mybatis替代JDBC进行数据库的操作,使用Mybatis有以下步骤:
在编写代码那一部分,我们真正要做的往往只是执行SQL语句那一部分即可,因为其他的部分在MyBatis官网已经提供了,我们只需要在官网CV过来然后修改一丢丢即可投入使用。
MyBatis官网:https://mybatis.org/mybatis-3/zh/index.html
在新建好的模块的工程目录下可以找到一个pom.xml的配置文件,我们需要在该配置文件中添加相应的依赖坐标
<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.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>MyBstis_DemoartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.5version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.46version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.20version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
<version>1.2.3version>
dependency>
dependencies>
project>
创建好模块和导入坐标之后,我们需要编写Mybatis核心配置文件来实现数据库的连接
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.kang.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&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="com/kang/mapper/UserMapper.xml"/>
mappers>
configuration>
在Mybatis核心配置文件中的mappers标签,设置了一个UserMapper.xml的SQL映射文件,那么创建一个SQL的映射文件
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="test">
<select id="selectAll" resultType="com.kang.pojo.User">
select * from tb_user;
select>
mapper>
首先定义相对应得POJO类
package com.kang.pojo;
/**
* @ClassName User
* @Description TODO
* @Author kang
* @Date 2022/1/17 下午 4:05
* @Version 1.0
*/
public class User {
private Integer id;
private String username;
private String password;
private String gender;
private String addr;
public User(Integer id, String username, String password, String gender, String addr) {
this.id = id;
this.username = username;
this.password = password;
this.gender = gender;
this.addr = 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 + '\'' +
'}';
}
}
接着编写测试类代码
package com.kang;
import com.kang.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;
/**
* @ClassName MyBatisDemo
* @Description TODO
* @Author kang
* @Date 2022/1/17 下午 4:23
* @Version 1.0
*/
public class MyBatisDemo {
public static void main(String[] args) throws IOException {
//1、加载mybatis核心配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行sql,通过名称空间来识别执行哪一个sql语句
List<User> users = sqlSession.selectList("test.selectAll");
System.out.println(users);
//释放资源
sqlSession.close();
}
}
运行程序
使用上面的方法对数据库进行操作的时候不难发现,这当中依然存在着硬编码的问题,那么当我们用Mapper代理开发的时候就可以更好地解决这一问题。
使用Mapeer代理开发需要完成以下步骤:
代码如下:
package com.kang.mapper;
import com.kang.pojo.Brand;
import com.kang.pojo.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* @ClassName UserMapper
* @Description TODO
* @Author kang
* @Date 2022/1/18 下午 3:52
* @Version 1.0
*/
public interface BrandMapper {
/**
* 查询所有
*/
public List<Brand> selectAll();
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kang.mapper.BrandMapper">
<select id="selectAll" resultMap="brandResultMap">
select *
from mybatis.tb_brand;
select>
mapper>
package com.kang;
import com.kang.mapper.UserMapper;
import com.kang.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;
/**
* @ClassName MyBatisDemo2
* @Description TODO
* @Author kang
* @Date 2022/1/18 下午 3:55
* @Version 1.0
*/
public class MyBatisDemo2 {
public static void main(String[] args) throws IOException {
//1、加载mybatis核心配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper接口的代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.selectAll();
System.out.println(users);
//释放资源
sqlSession.close();
}
}
注意:
如果Mapper接口名和SQL映射名称相同的时候,并在同一目录下,则可以使用扫描包的方式简化SQL映射文件加载
<typeAliases>
<package name="com.kang.pojo"/>
typeAliases>
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.kang.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&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="com/kang/mapper/UserMapper.xml"/>
mappers>
configuration>
配置MyBatis核心配置文件的时候需要注意各个标签的前后顺序
MyBatis核心配置文件的顶层结构如下:
在对应的xml文件中加入sql标签
<sql id="brand_column">
id, brand_name as brandName, company_name as companyName, ordered, description, status
sql>
id是唯一标识,引用时候也是根据该值进行引用的
使用该别名的时候,需要在原sql语句引用上述sql片段,refid指定上述sql片段的id值
<select id="selectAll" resultType="brand">
select
<include refid="brand_column" />
from tb_brand;
select>
使用sql片段的方式可以解决上述问题,但是这当中难免也存在问题,比如我们只需要查询其中一个字段段的时候,又需要重新编写一个sql片段,这样代码会显得非常冗余。
在映射配置文件中,使用resultMap定义字段和属性的映射关系
<resultMap id="brandResultMap" type="com.kang.pojo.Brand">
<result column="brand_name" property="brandName"/>
<result column="company_name" property="companyName"/>
resultMap>
而sql语句可以正常编写,但是之前的resultType换为resultMap
<select id="selectAll" resultMap="brandResultMap">
select *
from mybatis.tb_brand;
select>
在相对应的接口定义id查询数据的方法
/**
* 通过id查看详情
*/
Brand selectById(int id);
编写SQL语句
<select id="selectById" resultMap="brandResultMap">
select *
from mybatis.tb_brand
where id = #{id};
select>
其中花括号的id要与接口中的参数一致
编写测试类
@Test
public void testSelectById() throws IOException {
//传入参数
int id = 1;
//1.加载mybatis的核心配置文件,从而获取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语句的xml文件中,当我们使用小于(<)或者大于(>)的时候,会报错
针对这种情况下,有两种解决方案
转义字符
比如以下就是小于号的转移字符
<
使用来将小于包裹,表明这是一个小于的符号,这样计算机就不会报错了
在实际开发中,我们难免会遇到多条件查询,也就是查询的条件有多个,而Mybatis针对多参数有多种实现
List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName,@Param("brandName") String brandName);
List<Brand> selectByCondition(Brand brand);
List selectByCondition(Map map);
//接收参数
int status = 1;
String companyName = "华为";
String brandName = "华为";
// 处理参数
companyName = "%" + companyName + "%";
brandName = "%" + brandName + "%";
上述的功能实现无非还是有点硬编码的味道,当我们查询的内容不一样的时候,则需要修改SQL语句,极度不方便我们开发
针对这种情况,Mybatis对动态SQL提供了很强的支撑:
- If
- choose(when,otherwise)
- trim(where,set)
- foreach
if标签:条件判断标签
test属性:逻辑表达式
<select id="selectByCondition" resultMap="brandResultMap">
select *
from mybatis.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>
可以看到,像上面的代码,倘若status不为空,则sql语句为where and status = XXXX,这样的sql语句明显是错误的,因为为了解决这个问题,我们可以在前面加一个1 = 1,这样就不会出错了
像上面这也的 1=1 这样解决这个问题,虽然可以简单除暴地解决,但是依然不是很好。
Mybatis早就想到这个问题,它向我们用户提供了where标签
其作用是:
- 替换where关键词
- 会动态去掉第一个条件前的and
- 如果所有参数没有值则不加where关键词
注意:
- 每个条件前面都要加上and关键词
<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>
在实际开发中,查询的时候难免可以选择查询条件来查询,因此Mybatis提供了choose(when,otherwise)来实现动态但条件查询
<select id="selectByConditionSingle" resultMap="brandResultMap">
select *
from mybatis.tb_brand
<where>
<choose>
<when test="status != null">
status = #{status}
when>
<when test="companyName != null and companyName != '' ">
company_name like #{companyName}
when>
<when test="brandName != null and brandName != '' ">
brand_name like #{brandName}
when>
<otherwise>
1=1
otherwise>
choose>
where>
select>
@Test
public void selectByConditionSingle() throws IOException {
//传入参数
int status = 1;
String companyName = "华为";
String brandName = "华为";
//处理参数
companyName = "%" + companyName + "%";
brandName = "%" + brandName + "%";
Brand brand = new Brand();
//status查询
brand.setStatus(status);
// brand.setCompanyName(companyName);
// brand.setBrandName(brandName);
// Map stringStringMap = new HashMap();
// stringStringMap.put("status", status);
// stringStringMap.put("companyName", companyName);
// stringStringMap.put("brandName", brandName);
//1.加载mybatis的核心配置文件,从而获取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();
}
}
useGeneratedKeys 为true的作用 :获取插入记录的自增长字段值。
<insert id="add" useGeneratedKeys="true" keyProperty="id">
insert into mybatis.tb_brand (brand_name, company_name, ordered, description, status)
values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status});
insert>
set标签主要是用来解决每个更新语句后面的“,”导致无法实现动态的sql语句
set标签可以用于动态包含需要更新的列,忽略其它不更新的列。
<update id="update">
update mybatis.tb_brand
<set>
<if test="brandName != null and brandName != '' ">
brand_name = #{brandName},
if>
<if test="companyName != null and companyName != '' ">
company_name = #{companyName}
if>
set>
where id = #{id};
update>
@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.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();
//设置事务提交为ture
//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();
}
}
简单的删除操作非常简单实现,只需要实现在相应的映射文件中编写删除的xml代码
<delete id="deleteById">
delete from tb_brand where id = #{id};
delete>
接着在对应的接口编写对应的删除方法即可
void deleteById(int id);
批量删除则需要将要删除的东东存放在一个数组里,知识映射文件中的sql语句编写有点不一样
接口中的方法
void deleteByIds(@Param("ids")int[] ids);
而在映射文件中的sql语句,则需要使用foreach标签来实现
foreach标签有五个属性
- collection属性
- mybatis会将数组参数封装成一个Map集合
- 默认为:array = 数组
- 使用@Param注解改变map集合的默认key的名称
- item属性:本次迭代获取到的元素
- spearator属性:集合项迭代之间的分隔符。 foreach 标签不会错误地添加多余的分隔符。也就是最后一次迭代不会加 分隔符。
- open 属性:该属性值是在拼接SQL语句之前拼接的语句,只会拼接一次
- close 属性:该属性值是在拼接SQL语句拼接后拼接的语句,只会拼接一次
<delete id="deleteByIds">
delete
from mybatis.tb_brand
where id in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
foreach> ;
delete>
POJO类型
直接使用。要求属性名和参数占位符名称一致
Map集合类型
直接使用。要求map集合的键名和参数占位符名称一致
Collection集合类型
Mybatis会将集合封装到map集合中,如下
map.put("arg0", collection集合);
map.put("collection",collection集合);
可以使用@Param注解替换map集合中的默认的arg键名
List集合
Mtbatis会将集合封装到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类型,占位符叫什么都可以,尽量做到见名知其意即可
接口参数是多个时,在每个参数上都使用 @Param 注解。这样代码的可读性更高。
crud是指在做计算处理时的增加(Create)、检索(Retrieve)、更新(Update)和删除(Delete)几个单词的首字母简写。crud主要被用在描述软件系统中数据库或者持久层的基本操作功能
@Select(value = "select * from tb_user where id = #{id}")
public User select(int id);
Mybatis 针对 CURD 操作都提供了对应的注解,已经做到见名知意。如下:
``java
map.put(“arg0”, collection集合);
map.put(“collection”,collection集合);
可以使用@Param注解替换map集合中的默认的arg键名
4. List集合
Mtbatis会将集合封装到map集合中,如下:
```java
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类型,占位符叫什么都可以,尽量做到见名知其意即可