文章目录
MyBatista入门到入土——带你走进Mybatis的世界
Hibernate
ORM
对象关系映射,就是将数据库中的表和java中的对象建立映射,可以让我们操作对象来间接的操作数据库
Hibernate
可以通过java对象来间接的操作数据库。java对象就相当于数据库中表的代理一样。
对于开发者来说就是隐藏了底层jdbc和db的交互过程。
- 优点
- 简化jdbc操作
- 对于开发者来说不需要关心sql值需要取操作对象即可。
- 代码移植性比较好,会自动自适应
- 开发效率高
- 缺点
- sql优化艰难,很难干预需要执行的sql
- 入门容易,进阶难
- 对于复杂的动态sql,代码需要很多判断以及组装,动态sql 这块支持欠缺。
持久化
持久化是将程序数据在持久状态和瞬时状态间转换的机制
- 即把数据(内存中的对象)保存到可永久保存的存储设备中(磁盘)。
- 主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中
- JDBC就是一种持久化机制。文件IO也是一种持久化机制
作用——为了解决内存本身的缺陷
- 内存断电后数据会丢失
- 内存过于昂贵以及维护成本较高。
持久层
什么是持久层
- 完成持久化工作的代码块—>dao层【DAO数据访问对象】
- 大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘中,持久化的时间过长则大多数通过各种关系数据库来完成
- 用来操作数据库存在的
为什么需要MyBatista
- MyBatista就是进行数据和数据库之间的交互
- 通过使用MyBatista框架可以减少重复代码,提高开发效率
- MyBatista是一个半自动化的ORM框架(对象关系映射)
- MyBatista优点:
- 无侵入:只做增强不做改变,引入它不会对工程产生影响
- 损耗小:启动即会自动注入基本的CURD,性能基本无消耗,直接面向对象操作
- 强大的CRUD:内置通用Mapper、通用Service。
- 支持Lambda形式调用:通过Lambda表达式,方便的编写各种查询条件。
- 支持主键自动生成:
- 解除sql和程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,便于维护
- 提供xml标签,支持编写动态sql
框架结构
Mybatis常用示例
思路:搭建环境——导入MyBatista——编写代码——测试
最后成品图:
准备数据库
/*创建数据库zhonghubatis*/
DROP DATABASE IF EXISTS `zhonghubatis`;
CREATE DATABASE `zhonghubatis`;
USE `zhonghubatis`;
/*创建表结构*/
DROP TABLE IF EXISTS `zhonghu_user`;
CREATE TABLE zhonghu_user (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键,用户id,自动增长',
`name` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '姓名',
`age` SMALLINT NOT NULL DEFAULT 1 COMMENT '年龄',
`salary` DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '薪水'
) COMMENT '用户表';
SELECT * FROM zhonghu_user;
首先我们先创建一个zhonghubatis数据库,然后创建一个用户表,其中包含四个字段,其中id为自增长。
为了演示mybatis我们这张表要满足一下几个条件
- 实现一个通用的插入操作:支持动态插入,可以更具传入的字段的值,动态生成所需要的insert
- 批量插入功能
- 实现一个通用的更新操作:支持动态更新操作,可以根据传入的字段,动态生成所需要的各种update语句
- 实现一个通用的查询操作:支持各种组合条件查询,支持排序、分页、支持返回列控制等各种复杂的查询操作
创建maven项目
创建一个maven项目以及一个子项目mybatis-01-hello如图所示
引入mybatis依赖
在父maven文件中:
<?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>com.zhonghu</groupId>
<artifactId>zhonghuMabits</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>mybatis-01-hello</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 配置maven编译的时候采用的编译器版本 -->
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
<!-- 指定源代码是什么版本的,如果源码和这个版本不符将报错,maven中执行编译的时候会用到这个配置,默认是1.5,这个相当于javac命令后面的-source参数 -->
<maven.compiler.source>1.8</maven.compiler.source>
<!-- 该命令用于指定生成的class文件将保证和哪个版本的虚拟机进行兼容,maven中执行编译的时候会用到这个配置,默认是1.5,这个相当于javac命令后面的-target参数 -->
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
在mybatis-01-hello 中
<?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">
<parent>
<artifactId>zhonghuMabits</artifactId>
<groupId>com.zhonghu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mybatis-01-hello</artifactId>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
</project>
这样我们就引入了mybatis所需的包、mysql jdbc驱动、lombok(可以自动生成一些get、set方法)、单元测试需要的Junit包、日志输出所需的logback包。
配置logback
为了在mybatis运行过程中查看日志,诸如sql信息、sql参数、执行的结果等信息。
创建在子文件夹下的src/main/resources中新建logback.xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.zhonghu" level="debug" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
</configuration>
创建mybatis相关文件
user.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">
<mapper namespace="com.zhonghu.mybatis.UserMapper">
<!-- 插入 -->
<insert id="insert" parameterType="com.zhonghu.mybatis.UserModel" keyProperty="id" useGeneratedKeys="true">
<![CDATA[ INSERT INTO `zhonghu_user` ]]>
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id!=null">
<![CDATA[ `id`, ]]>
</if>
<if test="name!=null">
<![CDATA[ `name`, ]]>
</if>
<if test="age!=null">
<![CDATA[ `age`, ]]>
</if>
<if test="salary!=null">
<![CDATA[ `salary`, ]]>
</if>
</trim>
<![CDATA[ VALUES ]]>
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id!=null">
<![CDATA[ #{id}, ]]>
</if>
<if test="name!=null">
<![CDATA[ #{name}, ]]>
</if>
<if test="age!=null">
<![CDATA[ #{age}, ]]>
</if>
<if test="salary!=null">
<![CDATA[ #{salary}, ]]>
</if>
</trim>
</insert>
<!-- 批量插入 -->
<insert id="insertBatch" parameterType="map">
<![CDATA[ INSERT INTO `zhonghu_user` (`id`, `name`, `age`, `salary`) VALUES ]]>
<foreach collection="list" separator="," item="item">
(#{item.id}, #{item.name}, #{item.age}, #{item.salary})
</foreach>
</insert>
<!-- 更新 -->
<update id="update" parameterType="com.zhonghu.mybatis.UserModel">
<![CDATA[ UPDATE `zhonghu_user` ]]>
<set>
<if test="name!=null">
<![CDATA[ `name` = #{name}, ]]>
</if>
<if test="age!=null">
<![CDATA[ `age` = #{age}, ]]>
</if>
<if test="salary!=null">
<![CDATA[ `salary` = #{salary}, ]]>
</if>
</set>
<where>
<if test="id!=null">
<![CDATA[ AND `id` = #{id} ]]>
</if>
</where>
</update>
<!-- 更新 -->
<update id="updateByMap" parameterType="map">
<![CDATA[ UPDATE `zhonghu_user` ]]>
<set>
<if test="name!=null">
<![CDATA[ `name` = #{name}, ]]>
</if>
<if test="age!=null">
<![CDATA[ `age` = #{age}, ]]>
</if>
<if test="salary!=null">
<![CDATA[ `salary` = #{salary}, ]]>
</if>
</set>
<where>
<if test="id!=null">
<![CDATA[ AND `id` = #{id} ]]>
</if>
</where>
</update>
<!-- 删除 -->
<delete id="delete" parameterType="map">
<![CDATA[
DELETE FROM `zhonghu_user`
]]>
<where>
<if test="id!=null">
<![CDATA[ AND `id` = #{id} ]]>
</if>
</where>
</delete>
<!-- 查询记录 -->
<select id="getModelList" parameterType="map" resultType="com.zhonghu.mybatis.UserModel">
<![CDATA[
SELECT
]]>
<choose>
<when test="tableColumnList!=null and tableColumnList.size() >= 1">
<foreach collection="tableColumnList" item="item" separator=",">
<![CDATA[ ${item} ]]>
</foreach>
</when>
<otherwise>
<![CDATA[
`id`,
`name`,
`age`,
`salary`
]]>
</otherwise>
</choose>
<![CDATA[
FROM
`zhonghu_user` a
]]>
<where>
<if test="id!=null and id.toString()!=''">
<![CDATA[ AND a.`id` = #{id} ]]>
</if>
<if test="idList!=null and idList.size() >= 1">
<![CDATA[ AND a.`id` IN ]]>
<foreach collection="idList" item="item" open="(" separator="," close=")">
<![CDATA[ #{item} ]]>
</foreach>
</if>
<if test="name!=null and name.toString()!=''">
<![CDATA[ AND a.`name` = #{name} ]]>
</if>
<if test="age!=null and age.toString()!=''">
<![CDATA[ AND a.`age` = #{age} ]]>
</if>
<if test="salary!=null and salary.toString()!=''">
<![CDATA[ AND a.`salary` = #{salary} ]]>
</if>
<if test="nameLike!=null and nameLike.toString()!=''">
<![CDATA[ AND a.`name` like '%${nameLike}%' ]]>
</if>
<if test="salaryGte!=null and salaryGte.toString()!=''">
<![CDATA[ AND a.`salary` >= #{salaryGte} ]]>
</if>
</where>
<if test="sort!=null and sort.toString()!=''">
<![CDATA[ order by ${sort} ]]>
</if>
<if test="skip!=null and pageSize!=null">
<![CDATA[ LIMIT #{skip},#{pageSize} ]]>
</if>
</select>
</mapper>
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>
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/zhonghubatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/user.xml"/>
</mappers>
</configuration>
注意数据库连接中的url 因为mysql是8.0版本的,所以需要添加时区用& amp;来连接
UserMapper接口
package com.zhonghu.mybatis;
import java.util.List;
import java.util.Map;
/**
* Created with IntelliJ IDEA
* Description:
* Author: sun mingzhi
* Date: 2020/6/3 11:17
* Company: Inspur
*/
public interface UserMapper {
// 插入用户信息
void insert(UserModel userModel);
// 批量插入用户信息
void insertBatch(List<UserModel> userModelList);
// 更新用户记录
int update(UserModel userModel);
// 通过map来更新用户记录
int updateByMap(Map<String,Object>map);
// 通过map来删除用户记录
int delete(Map<String,Object>map);
// 查询用户列表
List<UserModel> getModelList(Map<String,Object>map);
}
UserModel类
package com.zhonghu.mybatis;
import lombok.*;
/**
* Created with IntelliJ IDEA
* Description:
* Author: sun mingzhi
* Date: 2020/6/3 11:22
* Company: Inspur
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
public class UserModel {
private Long id;
private String name;
private Integer age;
private Double salary;
}
在其中使用了很多lombak注解,通过这些注解,lombak可以帮助我们自动生成上面字段的get、set方法,无参构造方法,有参构造方法、builder模式构件对象代码、重写toString方法。
这些都在代码编译为字节码前写进去。
UserUtile类
package com.zhonghu.mybatis;
import lombok.extern.slf4j.Slf4j;
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;
/**
* Created with IntelliJ IDEA
* Description:
* Author: sun mingzhi
* Date: 2020/6/3 13:56
* Company: Inspur
*/
@Slf4j
public class UserUtil {
private static SqlSessionFactory sqlSessionFactory = build();
public static SqlSessionFactory build(){
try{
return new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}catch (IOException e){
log.error(e.getMessage(),e);
throw new RuntimeException(e);
}
}
@FunctionalInterface
public interface SessionCall<O>{
O call(SqlSession session) throws Exception;
}
@FunctionalInterface
public interface MapperCall<T,O>{
O call(T mapper)throws Exception;
}
public static <T, O> O callMapper(Class<T> tClass,MapperCall<T,O> mapperCall) throws Exception{
return call(session-> mapperCall.call(session.getMapper(tClass)));
}
public static <O> O call(SessionCall<O> sessionCall)throws Exception{
try(SqlSession session = sqlSessionFactory.openSession(true);){
return sessionCall.call(session);
}
}
}
创建单元测试类UserMapperTest
UserMapperTest
package com.zhonghu.mybatis;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.*;
import java.util.stream.Collectors;
/**
* Created with IntelliJ IDEA
* Description:
* Author: sun mingzhi
* Date: 2020/6/3 14:14
* Company: Inspur
*/
@Slf4j
public class UserMapperTest {
//动态插入
@Test
public void insert() throws Exception {
UserModel userModel1 = UserModel.builder().name("冢狐java").build();
UserUtil.callMapper(UserMapper.class, mapper -> {
mapper.insert(userModel1);
return null;
});
log.info("插入结果:{}", this.getModelById(userModel1.getId()));
log.info("---------------------");
UserModel userModel2 = UserModel.builder().name("冢狐").age(18).salary(10000.0).build();
UserUtil.callMapper(UserMapper.class, mapper -> {
mapper.insert(userModel2);
return null;
});
log.info("插入结果:{}", this.getModelById(userModel2.getId()));
}
//批量插入
@Test
public void insertBatch() throws Exception {
List<UserModel> userModelList = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
userModelList.add(UserModel.builder().name("冢狐java-" + i).age(30 + i).salary(10000.00 * i).build());
userModelList.add(UserModel.builder().name("Mybatis-" + i).age(30 + i).salary(10000.00 * i).build());
}
UserUtil.callMapper(UserMapper.class, mapper -> {
mapper.insertBatch(userModelList);
return null;
});
List<UserModel> userModelList1 = UserUtil.callMapper(UserMapper.class, mapper -> mapper.getModelList(null));
log.info("结果:{}", userModelList1);
}
//根据用户id删除数据
@Test
public void delete() throws Exception {
Map<String, Object> map = new HashMap<>();
//需要删除的用户id
map.put("id", 1);
Integer count = UserUtil.callMapper(UserMapper.class, mapper -> mapper.delete(map));
log.info("删除行数:{}", count);
}
//动态更新
@Test
public void update() throws Exception {
//将userId=10的name修改为:修改测试者
Long userId1 = 10L;
Integer count = UserUtil.callMapper(UserMapper.class, mapper -> mapper.update(UserModel.builder().id(userId1).name("修改测试者").build()));
log.info("更新行数:{}", count);
log.info("---------------------");
//将userId=3的name修改为:修改测试者2,薪水为:1234.56
Long userId2 = 11L;
count = UserUtil.callMapper(UserMapper.class, mapper -> mapper.update(UserModel.builder().id(userId2).name("修改测试者2").salary(1234.56D).build()));
log.info("更新行数:{}", count);
}
//按用户id查询
public UserModel getModelById(Long userId) throws Exception {
//查询指定id的数据
Map<String, Object> map = new HashMap<>();
map.put("id", userId);
return UserUtil.callMapper(UserMapper.class, mapper -> {
List<UserModel> userModelList = mapper.getModelList(map);
if (userModelList.size() == 1) {
return userModelList.get(0);
}
return null;
});
}
//查询所有数据
@Test
public void getModelList1() throws Exception {
List<UserModel> userModelList = UserUtil.callMapper(UserMapper.class, mapper -> mapper.getModelList(null));
log.info("结果:{}", userModelList);
}
//查询多个用户id对应的数据
@Test
public void getModelListByIds() throws Exception {
List<Integer> idList = Arrays.asList(2, 3, 4).stream().collect(Collectors.toList());
Map<String, Object> map = new HashMap<>();
map.put("idList", idList);
List<UserModel> userModelList = UserUtil.callMapper(UserMapper.class, mapper -> mapper.getModelList(map));
log.info("结果:{}", userModelList);
}
//多条件 & 指定返回的列
@Test
public void getModelList2() throws Exception {
//查询姓名中包含冢狐java以及薪资大于1万的用户id、姓名
Map<String, Object> map = new HashMap<>();
map.put("nameLike", "冢狐java");
map.put("salaryGte", 10000.00D);
//需要返回的列
List<String> tableColumnList = new ArrayList<>();
tableColumnList.add("id");
tableColumnList.add("name");
map.put("tableColumnList", tableColumnList);
List<UserModel> userModelList = UserUtil.callMapper(UserMapper.class, mapper -> mapper.getModelList(map));
log.info("结果:{}", userModelList);
}
//条件过滤 & 排序 & 分页查询数据 & 只返回用户id、salary
@Test
public void getPage() throws Exception {
//查询姓名中包含冢狐java以及薪资大于1万的用户id,按照薪资倒叙,每页5条取第1页
Map<String, Object> map = new HashMap<>();
map.put("nameLike", "冢狐java");
map.put("salaryGte", 10000.00D);
//加入排序参数
map.put("sort", "salary desc");
//加入分页参数
int page = 1;
int pageSize = 5;
map.put("skip", (page - 1) * pageSize);
map.put("pageSize", pageSize);
//加入需要返回的列
List<String> tableColumnList = new ArrayList<>();
tableColumnList.add("id");
tableColumnList.add("salary");
map.put("tableColumnList", tableColumnList);
List<UserModel> userModelList = UserUtil.callMapper(UserMapper.class, mapper -> mapper.getModelList(map));
log.info("结果:{}", userModelList);
}
}
运行结果
数据库信息
用例分析
insert方法:
- 代码
//动态插入
@Test
public void insert() throws Exception {
UserModel userModel1 = UserModel.builder().name("冢狐java").build();
UserUtil.callMapper(UserMapper.class, mapper -> {
mapper.insert(userModel1);
return null;
});
log.info("插入结果:{}", this.getModelById(userModel1.getId()));
log.info("---------------------");
UserModel userModel2 = UserModel.builder().name("冢狐").age(18).salary(10000.0).build();
UserUtil.callMapper(UserMapper.class, mapper -> {
mapper.insert(userModel2);
return null;
});
log.info("插入结果:{}", this.getModelById(userModel2.getId()));
}
- 结果日志
主要操作
- 插入一条用户记录,只有name字段有值
- 去db中查询步骤1中插入的记录
- 插入一条用户记录,这次插入的记录所有字段都指定了值
- 去db中查询步骤3插入的记录
两次插入的字段的值不一样,因此产生的sql也不一样,是因为mapper.insert方法可以根据UserModel对象字段是否有值来组装我们西药的sql,即动态插入
案例总结
上面这些用例,基本能包含我们对db所需的大部分操作,动态sql处理方面体现的最为强力。
其中文件:user.xml是这个mybatis中的核心文件,我们需要写的sql以及判断逻辑基本都在这个xml中。