Mybatis入门
1. ORM简介
ORM(Object Relational Mapping)对象-关系映射框架,ORM的主要功能就是根据映射配置文件,完成数据在对象模型与关系模型之间的映射。
1.1 常见的ORM框架
1.1.1 Hibernate
Hibernate通过hbm.xml映射文件维护一个Java类与数据库表的映射关系,通过Hibernate的映射,Java开发人员可以用看待Java对象的角度去看待数据库表中的数据行,数据库中多有的表通过hbm.xml配置文件映射之后,都对应一个Java类。
1.1.2 JPA
JPA(Java Persistence API) 是EJB 3.0中持久化部分的规范,他可以脱离EJB的体系单独作为一个持久化规范进行使用。
1.1.3 Spring JDBC
严格来说,Spring JDBC并不能算一个ORM框架,它仅仅是使用模板方式对原生JDBC进行了一层非常薄的封装,使用Spring JDBC可以帮助开发人员屏蔽创建数据库连接对象,Statement对象,异常处理以及事务管理的重复性代码,提高开发效率。
1.1.4 MyBatis
MyBatis与前面介绍的持久化框架一样,可以帮助开发人员屏蔽底层重复性的原生JDBC代码,MyBatis通过映射配置文件或相应注解将ResultSet映射为Java对象,其映射规则可以嵌套其他映射规则以及子查询,从而实现复杂的逻辑,也可实现一对一,一对多,多对多映射以及双向映射。相比Hibemate更加轻量级,可控性也更高。
2.mybatis项目搭建
2.1 新建maven项目
利用idea直接新建项目,然后选择创建maven项目:
填好项目的名称、groupId:
新建完成之后的项目结构如下:
2.2 pom依赖
引入mybatis以及mysql驱动包
<?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.github.chengbin</groupId>
<artifactId>mybatis-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.3 准备数据库脚本
CREATE DATABASE t_user DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
use t_user;
CREATE TABLE `user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL COMMENT '名称',
`age` int(11) DEFAULT '0' COMMENT '年龄',
`sex` tinyint(4) NOT NULL DEFAULT '0' COMMENT '性别',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1140550911683260418 DEFAULT CHARSET=utf8;
2.4 mybatis配置文件
首先在src/main/resources目录下创建mybatis-config.xml配置文件,为了后续更快速的创建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 resource="jdbc.properties"></properties> -->
<!-- 全局参数 -->
<settings>
<!-- 使全局的映射器启用或禁用缓存。 -->
<setting name="cacheEnabled" value="true"/>
<!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。 -->
<!-- <setting name="lazyLoadingEnabled" value="true"/> -->
<!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。 -->
<setting name="aggressiveLazyLoading" value="true"/>
<!-- 是否允许单条sql 返回多个数据集 (取决于驱动的兼容性) default:true -->
<setting name="multipleResultSetsEnabled" value="true"/>
<!-- 是否可以使用列的别名 (取决于驱动的兼容性) default:true -->
<setting name="useColumnLabel" value="true"/>
<!-- 允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。 default:false -->
<setting name="useGeneratedKeys" value="false"/>
<!-- 指定 MyBatis 如何自动映射 数据基表的列 NONE:不隐射 PARTIAL:部分 FULL:全部 -->
<setting name="autoMappingBehavior" value="PARTIAL"/>
<!-- 这是默认的执行类型 (SIMPLE: 简单; REUSE: 执行器可能重复使用prepared statements语句;BATCH: 执行器可以重复执行语句和批量更新) -->
<setting name="defaultExecutorType" value="SIMPLE"/>
<!-- 使用驼峰命名法转换字段。 -->
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
<!-- 设置本地缓存范围 session:就会有数据的共享 statement:语句范围 (这样就不会有数据的共享 ) defalut:session -->
<setting name="localCacheScope" value="SESSION"/>
<!-- 设置但JDBC类型为空时,某些驱动程序 要指定值,default:OTHER,插入空值时不需要指定类型 -->
<setting name="jdbcTypeForNull" value="NULL"/>
</settings>
<typeAliases>
<package name="com.github.chengbin.mybatisdemo.entity"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="" value=""/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/t_user"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
配置简单讲解:
2.4.1、properties属性
properties元素可以通过resource或url加载外部properties文件中的属性,也可以直接设置property属性。然后在xml中就可以通过${属性名}进行引用替换。
<properties resource="jdbc.properties"></properties>
引用属性方式
${jdbc.driverClassName}
# MyBatis3.4.2开始,支持指定默认值
${jdbc.driverClassName:com.mysql.jdbc.Driver}
2.4.2、environments属性
一个项目经常需要在例如开发坏境、测试环境、预上线环境、生产环境中等不同环境中进行部署,每个环境所对应的参数是不一样的,myBatis 中可以通过 environment 来设置不同环境的属性。指定应用环境:
default.environment=dev
<environments default="${default.environment}">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
2.4.3、settings属性
设置MyBatis 全局参数,约定myBatis的全局行为
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
2.4.4、typeAliases属性
在myBatis中经常会用到java中类型,如sql块中的参数集中javaType、结果集映射中的javaType等 ,都要使用java 全路径名,可以通过typeAliases属性设置别名
<typeAliases>
<!--<typeAlias type="com.github.chengbin.mybatisdemo.entity.User" alias="User"></typeAlias>-->
<package name="com.cyan.pojo"></package>
</typeAliases>
2.4.5、typeHandlers属性
持久层框架其中比较重要的工作就是处理数据的映射转换,把java类型转换成jdbc类型的参数,又需要把jdbc类型的结果集转换成java类型。在mybatis中是通过TypeHandler接口来实现的。
自定义类型处理器
public class CustomHandler extends BaseTypeHandler<Long> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Long parameter, JdbcType jdbcType) throws SQLException {
//ps.setDate(i,new Date(parameter));
ps.setTimestamp(i,new Timestamp(parameter));
}
@Override
public Long getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getDate(columnName).getTime();
}
@Override
public Long getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getDate(columnIndex).getTime();
}
@Override
public Long getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getDate(columnIndex).getTime();
}
}
配置文件方式应用
<typeHandlers>
<typeHandler handler="com.github.chengbin.mybatisdemo.handler.CustomHandler"
javaType="long" jdbcType="TIMESTAMP" />
</typeHandlers>
注解方式应用
@MappedJdbcTypes(JdbcType.TIMESTAMP)
@MappedTypes(Long.class)
public class CustomHandler extends BaseTypeHandler<Long> {}
2.4.6、mappers映射器
<mappers>
<mapper resource="mapper/UserMapper.xml"></mapper>
<mapper resource="mapper/AccountMapper.xml"></mapper>
</mappers>
resource:基于classpath加载xml文件
class:基于接口加载
package:扫描包下所有class,然后进行加载
约定规则:
1)mapper中的namespace必须与对应的接口名称对应
2)通过class或package加载时,xml文件必须与接口在同一级目录
2.5 创建实体类和Mapper.xml文件
public class User implements Serializable {
private Long id;
private String name;
private Integer age;
private Integer sex;
private static final long serialVersionUID = 1L;
...
}
<?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.github.chengbin.mybatisdemo.dao.UserMapper">
<resultMap id="BaseResultMap" type="com.github.chengbin.mybatisdemo.entity.User">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="age" jdbcType="INTEGER" property="age" />
<result column="sex" jdbcType="TINYINT" property="sex" />
</resultMap>
<sql id="Base_Column_List">
id, name, age, sex
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from user
where id = #{id,jdbcType=BIGINT}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
delete from user
where id = #{id,jdbcType=BIGINT}
</delete>
<insert id="insert" parameterType="com.github.chengbin.mybatisdemo.entity.User"
keyProperty="id" keyColumn="id" useGeneratedKeys="true">
insert into user (id, name, age,
sex)
values (#{id,jdbcType=BIGINT}, #{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER},
#{sex,jdbcType=TINYINT})
</insert>
<insert id="insertSelective" parameterType="com.github.chengbin.mybatisdemo.entity.User"
keyProperty="id" keyColumn="id" useGeneratedKeys="true">
insert into user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="name != null">
name,
</if>
<if test="age != null">
age,
</if>
<if test="sex != null">
sex,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=BIGINT},
</if>
<if test="name != null">
#{name,jdbcType=VARCHAR},
</if>
<if test="age != null">
#{age,jdbcType=INTEGER},
</if>
<if test="sex != null">
#{sex,jdbcType=TINYINT},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.github.chengbin.mybatisdemo.entity.User">
update user
<set>
<if test="name != null">
name = #{name,jdbcType=VARCHAR},
</if>
<if test="age != null">
age = #{age,jdbcType=INTEGER},
</if>
<if test="sex != null">
sex = #{sex,jdbcType=TINYINT},
</if>
</set>
where id = #{id,jdbcType=BIGINT}
</update>
<update id="updateByPrimaryKey" parameterType="com.github.chengbin.mybatisdemo.entity.User">
update user
set name = #{name,jdbcType=VARCHAR},
age = #{age,jdbcType=INTEGER},
sex = #{sex,jdbcType=TINYINT}
where id = #{id,jdbcType=BIGINT}
</update>
<select id="findByName" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from user
where name = #{name,jdbcType=VARCHAR}
</select>
</mapper>
1)Mapper中的常见元素
resultMap – 结果集映射
select – 查询语句
insert – 插入语句
cache – 对给定命名空间的缓存配置
parameterMap - 参数集映射
update – 更新语句
delete – 删除语句
cache-ref - 指定缓存命名空间
sql – 可被其他语句引用的可重用语句块。
2)select中的常见属性
id - 语句块的唯一标识,与接口中方法名称对应
parameterMap - 参数集映射
parameterType - 参数java类型
resultMap - 返回结果映射
resultType - 返回结果java类型
statementType - 预处理类型
timeout - 超时时间
flushCache - 每次调用都会刷新一二级缓存
useCache - 是否保存至二级缓存当中去
3)insert&update&delete中的常见属性
id - 语句块的唯一标识,与接口中方法名称对应
parameterMap - 参数集映射
parameterType - 参数java类型
statementType - 预处理类型
timeout - 超时时间
flushCache- true每次调用都会刷新一二级缓存
# insert、update还具有如下三个属性(delete则没有)
keyProperty - 主键对应的java属性,多个用 逗号分割
keyColumn - 主键列,多个用逗号分割
useGeneratedKeys - 插入成功后可以获取到数据库自动生成的主键值
整体结构如下:
2.6 编写测试用例
package com.github.chengbin.mybatisdemo;
import com.github.chengbin.mybatisdemo.dao.UserMapper;
import com.github.chengbin.mybatisdemo.entity.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;
/**
* @description:
* @author: zhangcb
* @create: 2020-04-17 15:33
**/
public class App {
public static void main(String[] args) {
try {
// 1.读取mybatis配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 2.根据配置文件创建SqlSessionFactory
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(inputStream);
// 3.通过SqlSessionFactory获取SqlSession,然后才能通过SqlSession与数据库进行交互
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4.通过jdk的动态代理获取UserMapper接口的代理类
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 5.调用接口
List<User> userList = mapper.findByName("zhangsan");
for (User user : userList) {
System.out.println("name:" + user.getName() + ",age:" + user.getAge());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
执行结果如下: