文章目录
简介
MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
基本使用
- 首先引入jar包
<!--jar包依赖-->
<dependencies>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!--junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!--log4j日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
- 我们以一个用户的增删查改为例子
创建一个java实体类user对象
public class User {
private Integer id;
private String name;
private String password;
private char sex;
private String email;
public User() {
}
public User(Integer id, String name, String password, char sex, String email) {
this.id = id;
this.name = name;
this.password = password;
this.sex = sex;
this.email = email;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", sex=" + sex +
", email='" + email + '\'' +
'}';
}
}
设置好getter setter方法
- mapper接口
public interface UserMapper {
/**
* 添加一个用户
*/
int insertUser();
/**
* 更新一个用户
*/
int updateUser();
/**
* 删除一个用户
*/
int deleteUser();
/**
* 根据id号查询用户
*/
User getUserById(Integer id);
/**
* 获取所有user
*/
List<User> getAllUser();
}
- 创建数据库,和user对应的表 以便下面我们对他进行增删查改
5.下面就是创建mybatisConfig配置文件
<?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"/>
<!--
设置类型别名
类型别名不区分大小写
alias可以设置可以不设置 当不设置alias时 alias默认为class的名称 user不去区分大小写
-->
<typeAliases>
<typeAlias type="cn.luoyudi.mybatis.pojo.User" alias="User"/>
<!--扫描包下面的所有类型 设置包名 以包为单位 将包下 所有的类型都设置为默认的类型名称 即类名不区分大小写-->
<package name="cn.luoyudi.mybatis.pojo"/>
</typeAliases>
<!--配置连接数据库的环境-->
<environments default="development">
<!--设置环境-->
<environment id="development">
<!--
事务管理 存在两个值'jdbc'表示当前环境中,使用的是jdbc的事务管理方式
'managed' 表示被管理
-->
<transactionManager type="JDBC"/>
<!--
数据源设置
POOLED表示使用数据库连接池
unPOOLED表示不使用数据库连接池
jndi使用上下文中配置的连接池
-->
<dataSource type="POOLED">
<!--使用${}引用配置文件中的value-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<!--<mapper resource="mapper/UserMapper.xml"/>-->
<!--以包为单位扫描
注意mapper.xml映射文件需要在接口同目录下并且名称一致
-->
<package name="cn.luoyudi.mybatis.mapper"/>
</mappers>
</configuration>
- 配置mapper.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">
<!--绑定UserMapper-->
<mapper namespace="cn.luoyudi.mybatis.mapper.UserMapper">
<!--int insertUser();-->
<insert id="insertUser">
insert into t_user
values (null, 'admin', 'admin', '男', 'admin@qq.com');
</insert>
<!--int updateUser();-->
<update id="updateUser">
update t_user
set name = '张三'
where id = 2;
</update>
<!--int deleteUser();-->
<delete id="deleteUser">
delete
from t_user
where id = 3;
</delete>
<!--User getUserById(Integer id);-->
<select id="getUserById" resultType="cn.luoyudi.mybatis.pojo.User">
select *
from t_user
where id = 2;
</select>
<!--List<User> getAllUser();-->
<select id="getAllUser" resultType="User">
select *
from t_user;
</select>
</mapper>
- 测试方法
@Test
public void insertUser() throws IOException {
//加载核心配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//获取sqlSessionFactoryBuilder
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取sqlSessionFactory
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
//获取sqlSession 自动提交
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//获取mapper接口对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.insertUser();
//sqlSession.commit();
System.out.println(i);
}
mybatis工作流程
- 加载配置文件
- sqlSessionFactoryBuilder.build(inputStream);读取配置文件,新建一个sqlSession工厂
- sqlSessionFactory.openSession();工厂生产sqlsession
- sqlSession.getMapper(UserMapper.class);从sqlsession中传递接口的class,获取对应的mapper,
- mapper直接调用方法,就可以直接实现方法
在这个过程中,mybatis帮助我们创建了userMapperImpl对象,并且帮我们生成sql对应的方法,实现功能
各种查询操作
根据id查询用户信息
/**
* 根据id查询用户信息
*/
User selectUserById(@Param("id") Integer id);
<!--User selectUserById(Integer id);-->
<select id="selectUserById" resultType="user">
select *
from t_user
where id = #{id};
</select>
在简单的查询中,向sql语句中传递单个参数可以不用带@Param() 但是,为了记忆方便,我们都带上这个注解
在这个注解中配置的名称,就是在sql语句中引用的名称#{id};
resultType属性,是设置查询语句返回值而设定的属性,查询返回值是user对象,那就设置成user,前提是需要在mybatisConfig核心文件中配置
<typeAliases>
<package name="cn.luoyudi.mybatis.pojo"/>
</typeAliases>
这个属性就是设置实体类的别名使用的,因为如果不设置别名,他的引用就是实体类的全类名,非常长,所以我们使用设置别名,在这个属性中可以设置扫描包,也可以单个设置类名,在实体类上注明@Alias()注解就可以自定义起名,默认扫到的名称为首字母小写名称如User->user
查询所有用户信息
/**
* 查询所有用户信息
*/
List<User> getAllUser();
<!--List<User> getAllUser();-->
<select id="getAllUser" resultType="user">
select *
from t_user;
</select>
查询所有用户信息与上面的查询方法相同,
查询表中的总记录数
/**
* 查询表中的总记录数
*/
Integer getCount();
<!--Integer getCount();-->
<select id="getCount" resultType="java.lang.Integer">
select count(*)
from t_user;
</select>
查询结果条数返回的是一个integer类型的数字,所以resultType属性应当配置integer类型的全类名
值得要说的是,在mybatis初始化的时候,自动给我们设置了一些别名
如上图,所以这里也可以直接写作int
根据id查询用户的信息为一个map集合
/**
* 根据id查询用户的信息为一个map集合
*/
Map<String, Object> getUserMapById(@Param("id") Integer id);
<!--Map<String, Object> getUserMapById(@Param("id") Integer id);-->
<select id="getUserMapById" resultType="map">
select *
from t_user
where id = #{id};
</select>
就是将查询到的用户字段名为键字段值为键的值,
我们设置了一个map集合当作返回结果,这里使用的就是map集的别名,
获取所有用户都作为map
/**
* 获取所有用户都作为map
*/
//List<Map<String, Object>> getAllUserToMap();
@MapKey("id")
Map<String, Object> getAllUserToMap();
<!--List<Map<String, Object>> getAllUserToMap();-->
<select id="getAllUserToMap" resultType="map">
select *
from t_user;
</select>
这里查询到所有用户都作为map集合,并且将查询到的用户放在一个list集合中返回值需要改成List<Map<String, Object>>
或者另一种方式@MapKey()注解返回值仍然可以是Map<String, Object>此时,mybatis就会自己把我们配在注解中的属性的值当键,以键对应的查询结果为值放在新的map集合中
类似于@MapKey(“id”)Map<id字段对应的值,Map<String, Object>>
根据用户名进行模糊查询
/**
* 根据用户名模糊查询
*/
List<User> getUserByLike(@Param("username") String str);
<!--List<User> getUserByLike(String str);-->
<select id="getUserByLike" resultType="user">
select *
from t_user
-- where name like '%${username}%';
where name like concat('%',
#{username},
'%'
);
</select>
实现模糊查询,有两种sql语句可以使用一个使用#{}一个使用${}
首先说一下两种方式的区别
他们都可以取值,但是两种方式的区别是,#{}是预编译的,${}本质是占位符有sql注入的隐患
所以能使用#{}一定要使用#{},
先介绍使用${}的方式 ‘%${username}%’;直接拼接在like后面%%通配符,直接查询
然后是#{},使用#{}时因为我们是预编译,所以使用上面的写法会出现问题,此时我们要使用#{}可以使用mysql中的字符串拼接函数concat()将#{}和通配符相拼接
批量删除
/**
* 批量删除
*/
int deleteMoreUser(@Param("ids") String ids);
<!--int deleteMoreUser(@Param("ids") String ids);-->
<delete id="deleteMoreUser">
delete
from t_user
where id in (${ids});
</delete>
使用批量删除时,如果我们单独调用删除一条的语句需要循环好多次,会降低我们效率,所以使用mysql中的in语法,直接传递进去一个字符串,字符串里面的都直接删除
查询指定表中的数据
/**
* 查询指定表中的数据
*/
List<User> getUserByTableName(@Param("tableName") String tableName);
<!--List<User> getUserByTableName(@Param("tableName") String tableName);-->
<select id="getUserByTableName" resultType="user">
select *
from ${tableName};
</select>
查询表中的数据,直接输入进去表名即可
上面就是一些基本的操作实例
多对一和一对多关系
处理多对一映射关系
- 级联属性
<resultMap id="empAndDeptResultMap_1" type="emp">
<result property="dept.did" column="did"/>
<result property="dept.deptName" column="dept_name"/>
</resultMap>
不一样的属性全部配置到映射里面
- 使用association标签
association专门处理多对一关系,需要把属性单独配置出来,写法上与上面的方式几乎一样 只是多一个标签
只是使用association后,所有属性都需要进行映射
<resultMap id="empAndDeptResultMap_2" type="emp">
<id property="eid" column="eid"/>
<result property="empName" column="emp_name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<result property="email" column="email"/>
<!--property需要处理的属性名 javaType是实体类对象-->
<association property="dept" javaType="dept">
<id property="did" column="did"/>
<result property="deptName" column="dept_name"/>
</association>
</resultMap>
- 分步查询
<!--Emp getEmpAndDeptBySept_1(@Param("eid") Integer eid);-->
<select id="getEmpAndDeptBySept_1" resultMap="getEmpAndDeptBySeptResultMap_1">
select *
from t_emp
where eid = #{eid};
</select>
<resultMap id="getEmpAndDeptBySeptResultMap_1" type="emp">
<!--
select : 设置第二部查询sql语句唯一标识 类名加上方法名
column : 分布查询第二部的条件
-->
<association property="dept"
select="cn.luoyudi.mybatis.mapper.DeptMapper.getEmpAndDeptBySept_2"
column="did"/>
</resultMap>
处理一对多映射关系
略
动态sql
当sql语句where后面的条件语句有时满足有时不满足时,我们可以使用if标签,当满足text属性中的条件时,if中的语句会被粘贴在前面的语句后,当不满足条件时,将不会有效
<select id="getEmpByCondition_1" resultType="emp">
select * from t_emp where 1=1
<if test="empName != null and empName != ''">
and emp_name = #{empName}
</if>
<if test="age!=null and age!=''">
and age = #{age}
</if>
<if test="sex != null and sex!=''">
and sex = #{sex}
</if>
<if test="email != null and email != ''">
and email = #{email}
</if>
</select>
并且,为了语句不报错,需要在where后面加入一条恒成立的条件比如1=1
现在就可以动态生成sql语句了,但是仍然不太满足,因为where后面的条件冗余,所以有另外的标签,解决这个问题
where标签
<select id="getEmpByCondition_2" resultType="emp">
select * from t_emp
<where>
<if test="empName != null and empName != ''">
and emp_name = #{empName}
</if>
<if test="age!=null and age!=''">
and age = #{age}
</if>
<if test="sex != null and sex!=''">
and sex = #{sex}
</if>
<if test="email != null and email != ''">
and email = #{email}
</if>
</where>
</select>
可以看到,这时的语句减少了where后面的1=1,并且可以删除语句之前的and或者or,非常好用
接下来的标签,可以自定义把语句中前面或者后面添加或者删除指定的字符
<select id="getEmpByCondition" resultType="emp">
select * from t_emp
<trim prefix="" suffix="" prefixOverrides="" suffixOverrides="">
<if test="empName != null and empName != ''">
and emp_name = #{empName}
</if>
<if test="age!=null and age!=''">
and age = #{age}
</if>
<if test="sex != null and sex!=''">
and sex = #{sex}
</if>
<if test="email != null and email != ''">
and email = #{email}
</if>
</trim>
</select>
trim中有四个标签
prefix|suffix : 在语句前后添加指定语句
prefixOverrides|suffixOverrides : 在语句前后删除指定语句
例:
<select id="getEmpByCondition" resultType="emp">
select * from t_emp
<trim prefix="where" prefixOverrides="and|or">
<if test="empName != null and empName != ''">
and emp_name = #{empName}
</if>
<if test="age!=null and age!=''">
and age = #{age}
</if>
<if test="sex != null and sex!=''">
and sex = #{sex}
</if>
<if test="email != null and email != ''">
and email = #{email}
</if>
</trim>
</select>
逆向工程
逆向工程是根据表中的字段名称,自动生成java实体类的过程叫逆向工程,就是根据表生成实体类,简化了写实体类的过程
- 首先先添加依赖 在pom文件中添加生成类的插件
<!-- 控制Maven在构建过程中相关配置 -->
<build>
<!-- 构建过程中用到的插件 -->
<plugins>
<!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.0</version>
<!-- 插件的依赖 -->
<dependencies>
<!-- 逆向工程的核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
- 另外就是需要配置逆向工程的配置文件 并且 文件名称必须是generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--
targetRuntime: 执行生成的逆向工程的版本
MyBatis3Simple: 生成基本的CRUD(清新简洁版)
MyBatis3: 生成带条件的CRUD(奢华尊享版)
-->
<context id="DB2Tables" targetRuntime="MyBatis3">
<!-- 数据库的连接信息 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis"
userId="root"
password="mysql">
</jdbcConnection>
<!-- javaBean的生成策略 生成路径-->
<javaModelGenerator targetPackage="cn.luoyudi.mybatis.pojo" targetProject=".\src\main\java">
<!--是否能够使用子包->多层目录-->
<property name="enableSubPackages" value="true" />
<!--去掉字符串前后的空格-->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="cn.luoyudi.mybatis.mapper" targetProject=".\src\main\resources">
<!--子包-->
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="cn.luoyudi.mybatis.mapper" targetProject=".\src\main\java">
<!--子包-->
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
<!-- domainObjectName属性指定生成出来的实体类的类名 -->
<table tableName="t_emp" domainObjectName="Emp"/>
<table tableName="t_dept" domainObjectName="Dept"/>
</context>
</generatorConfiguration>
文件内部主要包括文件生成位置,配置表的名称,
以及生成策略,包括两种,一种是比较简洁的MyBatis3Simple策略,一种是比较豪华的MyBatis3策略
配置好之后需要执行插件生成实体类以及mapper映射文件插件名称为generate,双击运行
运行后就会在配置好的路径下生成文件,简洁版会生成实体类,mapper文件,豪华版还会生成实体类对应的example条件构建类
在执行有条件的查询语句时,就需要new一个对应的实体类的example对象进行配置,支持链式编程
@Test
public void testMBG() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//List<Emp> empList = mapper.selectByExample(null);
//empList.forEach(System.out::println);
EmpExample example = new EmpExample();
example.createCriteria().andEmpNameEqualTo("张三").andAgeGreaterThan(20);
example.or().andDidIsNotNull();
List<Emp> empList = mapper.selectByExample(example);
empList.forEach(System.out::println);
}
测试方法
分页插件pagehelper
分页插件主要是为了解决在分页时需要的包括页数,总记录数,页码等等信息需要自己计算出来,非常麻烦,为了解决上面的问题,需要分页插件,分页插件可以生成很多我们分页时的数据,直接调用即可
- 首先先导入xml配置文件
<dependencies>
<!--pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
</dependencies>
@Test
public void textPageHelper() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//开启分页
//Page<Object> page = PageHelper.startPage(3, 3);
Page<Object> page = PageHelper.startPage(3, 3);
List<Emp> emps = mapper.selectByExample(null);
//获取更多数据
PageInfo<Emp> pageInfo = new PageInfo<>(emps, 5);
//emps.forEach(System.out::println);
System.out.println(pageInfo);
}
常用数据 :
- pageNum:当前页的页码
- pageSize:每页显示的条数
- size:当前页显示的真实条数
- total:总记录数
- pages:总页数
- prePage:上一页的页码
- nextPage:下一页的页码
- isFirstPage/isLastPage:是否为第一页/最后一页
- hasPreviousPage/hasNextPage:是否存在上一页/下一页
- navigatePages:导航分页的页码数
- navigatepageNums:导航分页的页码,[1,2,3,4,5]