前言
SSM框架的最后一部分。
简介
mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精 去处理加载驱动、创建连接、创建statement等繁杂的过程。
mybatis通过xml 或注解 的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句。
最后mybatis框架执行sql并将结果映射为java对象并返回。采用ORM思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。
快速开发
步骤:
添加MyBatis的坐标
创建user
数据表
编写User
实体类
编写映射文件UserMapper.xml
编写核心文件SqlMapConfig.xml
编写测试类
用Maven新建一个项目,结构如下:
编辑pom.xml配置文件,添加MyBatis依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <dependencies > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.21</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.4.6</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > <scope > test</scope > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency > </dependencies >
在MySQL数据库中新建test
库,在其中新建user
表,分别创建id
(int,主键)、username
(varchar)、password
(varchar)字段,并添加数据:
在/src/main/java
目录下继续创建/quick/domain
子目录,在其中编写User
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class User { private int id; private String username; private String password; public int getId () { return id; } public void setId (int 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; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + '}' ; } }
在/src/main/resources
目录下创建quick/mapper
子目录,在其中编写UserMapper.xml
映射文件:
1 2 3 4 5 6 7 <?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 ="userMapper" > <select id ="findAll" resultType ="quick.domain.User" > select * from user </select > </mapper >
表头信息如下:
1 2 <?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" >
映射文件DTD约束头:<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper>
:根标签
namespace
:命名空间,与下面子标签语句的id一起组成查询的标识
子标签:<select>
、<insert>
、<update>
、<delete>
id:与上面的命名空间一起组成查询的标识
resultType
:查询结果对应的实体类型,指向的创建的实体类
在/src/main/resources
目录下编写sqlMapConfig.xml
配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?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 > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.cj.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/test?useUnicode=true& characterEncoding=utf-8& serverTimezone=Asia/Shanghai" /> <property name ="username" value ="root" /> <property name ="password" value ="root" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="quick/mapper/UserMapper.xml" > </mapper > </mappers > </configuration >
表头信息如下:
1 2 <?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" >
配置数据源环境以及加载上一步编写的映射文件
在/src/test/java
目录下创建test
子目录,在其中编写MyBatisTest
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 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 quick.domain.User;import java.io.IOException;import java.io.InputStream;import java.util.List;public class MyBatisTest { @Test public void test1 () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); List<User> userList = sqlSession.selectList("userMapper.findAll" ); System.out.println(userList); sqlSession.close(); } }
注意正确导包
执行测试类,可以看到打印出存储在数据库中的信息:
操作:增删改查
insert插入
插入语句使用<insert>
标签
在映射文件中使用parameterType
属性指定要插入的数据类型
Sql语句中使用#{实体属性名}
方式引用实体中的属性值
插入操作使用的API是sqlSession.insert(“命名空间.id”,实体对象);
插入操作涉及数据库数据变化,所以要使用sqlSession对象显示的提交事务, 即sqlSession.commit()
编写UserMapper.xml
映射文件:
1 2 3 4 5 6 7 8 9 10 <?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 ="userMapper" > <insert id ="save" parameterType ="quick.domain.User" > insert into user values(#{id},#{username},#{password}) </insert > </mapper >
编写MyBatisTest
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class MyBatisTest { @Test public void test2 () throws IOException { User user = new User(); user.setUsername("tom" ); user.setPassword("abc" ); InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); sqlSession.insert("userMapper.save" ,user); sqlSession.commit(); sqlSession.close(); } }
数据库设置id自增,因此这里没有设置user对象的id值
执行,刷新数据库,可以看到添加了“tom”的信息:
update修改
修改语句使用<update>
标签
修改操作使用的API是sqlSession.update(“命名空间.id”,实体对象);
编写UserMapper.xml
映射文件:
1 2 3 4 5 6 7 8 9 10 <?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 ="userMapper" > <update id ="update" parameterType ="quick.domain.User" > update user set username=#{username},password=#{password} where id=#{id} </update > </mapper >
编写MyBatisTest
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class MyBatisTest { @Test public void test3 () throws IOException { User user = new User(); user.setId(7 ); user.setUsername("lucy" ); user.setPassword("123" ); InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); sqlSession.update("userMapper.update" ,user); sqlSession.commit(); sqlSession.close(); } }
执行,刷新数据库,可以看到修改了id为7的信息:
delete删除
删除语句使用<delete>
标签
Sql语句中使用#{任意字符串}
方式引用传递的单个参数
删除操作使用的API是sqlSession.delete(“命名空间.id”,Object);
编写UserMapper.xml
映射文件:
1 2 3 4 5 6 7 8 9 10 <?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 ="userMapper" > <delete id ="delete" parameterType ="java.lang.Integer" > delete from user where id=#{id} </delete > </mapper >
编写MyBatisTest
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class MyBatisTest { @Test public void test4 () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); sqlSession.delete("userMapper.delete" ,7 ); sqlSession.commit(); sqlSession.close(); } }
测试,刷新数据库,可以看到id为7的数据被删除:
select查询
查询所有
查询语句使用<select>
标签
查询操作使用的API是sqlSession.selectList(“命名空间.id”);
编写UserMapper.xml
映射文件:
1 2 3 4 5 6 7 8 <?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 ="userMapper" > <select id ="findAll" resultType ="quick.domain.User" > select * from user </select > </mapper >
编写MyBatisTest
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class MyBatisTest { @Test public void test1 () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); List<User> userList = sqlSession.selectList("userMapper.findAll" ); System.out.println(userList); sqlSession.close(); } }
测试,打印查询结果:
查询单个
查询语句使用<select>
标签
查询操作使用的API是sqlSession.selectOne(“命名空间.id”,Object);
编写UserMapper.xml
映射文件:
1 2 3 4 5 6 7 8 <?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 ="userMapper" > <select id ="findById" resultType ="user" parameterType ="java.lang.Integer" > select * from user where id=#{id} </select > </mapper >
编写MyBatisTest
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class MyBatisTest { @Test public void test5 () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); User user = sqlSession.selectOne("userMapper.findById" , 1 ); System.out.println(user); sqlSession.close(); } }
测试,打印查询结果:
核心配置文件
层级关系
configuration配置
properties属性
settings设置
typeAliases类型别名
typeHandlers类型处理器
objectFactory对象工厂
plugins插件
environments环境
environment环境变量
transactionManager事务管理器
dataSource数据源
databaseIdProvider数据库厂商标识
mappers映射器
常用配置解析
environments标签
以如下数据库环境配置为例:
1 2 3 4 5 6 7 8 9 10 11 <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.cj.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/test?useUnicode=true& characterEncoding=utf-8& serverTimezone=Asia/Shanghai" /> <property name ="username" value ="root" /> <property name ="password" value ="root" /> </dataSource > </environment > </environments >
说明:
<environments>
支持多环境配置
<environments default="development">
:指定默认的环境名称
<environment id="development">
:指定当前环境的名称
<transactionManager type="JDBC"/>
:指定事务管理类型是JDBC。事务管理器(transactionManager)类型有两种:
JDBC
:这个配置就是直接使用JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
MANAGED
:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE应用服务器的上下文)。默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置 为false来阻止它默认的关闭行为。
<dataSource type="POOLED">
:指定当前数据源类型是连接池。数据源(dataSource)类型有三种:
UNPOOLED
:这个数据源的实现只是每次被请求时打开和关闭连接。
POOLED
:这种数据源的实现利用“池”的概念将JDBC连接对象组织起来。
JNDI
:这个数据源的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。
<property>
:基本参数配置
mapper标签
该标签的作用是加载映射的,加载方式有如下几种:
使用相对于类路径的资源引用,例如:<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
使用完全限定资源定位符(URL),例如:<mapper url="file:///var/mappers/AuthorMapper.xml"/>
使用映射器接口实现类的完全限定类名,例如:<mapper class="org.mybatis.builder.AuthorMapper"/>
将包内的映射器接口实现全部注册为映射器,例如:<package name="org.mybatis.builder"/>
properties标签
实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件(例如:jdbc.properties)
1 2 3 4 jdbc.driver =com.mysql.cj.jdbc.Driver jdbc.url =jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.username =root jdbc.password =root
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <properties resource ="jdbc.properties" > </properties > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <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 >
typeAliases标签
用于定义别名。
编辑sqlMapConfig.xml
配置文件:
1 2 3 4 <typeAliases > <typeAlias type ="quick.domain.User" alias ="user" > </typeAlias > </typeAliases >
注意:
将quick.domain.User
全限定名称起别名:user
该配置有顺序要求,必须放于<properties>
标签、<settings>
标签之后,<environments>
标签之前
接着就可以在UserMapper.xml
中使用user别名:
1 2 3 4 <select id ="findAll" resultType ="user" > select * from user </select >
mybatis框架已经设置好了一些常用的类型的别名:
别名
数据类型
string
String
long
Long
int
Integer
double
Double
boolean
Boolean
typeHandlers标签
无论是MyBatis在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成Java类型。下表描述了一些默认的类型处理器:
类型处理器
Java类型
JDBC类型
BooleanTypeHandler
java.lang.Boolean, boolean
数据库兼容的 BOOLEAN
ByteTypeHandler
java.lang.Byte, byte
数据库兼容的 NUMERIC 或 BYTE
ShortTypeHandler
java.lang.Short, short
数据库兼容的 NUMERIC 或 SHORT INTEGER
IntegerTypeHandler
java.lang.Integer, int
数据库兼容的 NUMERIC 或 INTEGER
LongTypeHandler
java.lang.Long, long
数据库兼容的 NUMERIC 或 LONG INTEGER
可以重写类型处理器或创建自己的类型处理器来处理不支持的或非标准的类型。具体做法为:实现org.apache.ibatis.type.TypeHandler
接口,或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler
,然后可以选择性地将它映射到一个JDBC类型。
例如需求:一个Java中的Date数据类型,想将之存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换。
首先搭建环境:
编辑User
类,新添加birthday
变量,并设定getter和setter方法,重新生成toString()
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class User { private int id; private String username; private String password; private Date birthday; public Date getBirthday () { return birthday; } public void setBirthday (Date birthday) { this .birthday = birthday; } public int getId () { return id; } public void setId (int 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; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", birthday=" + birthday + '}' ; } }
在MySQL数据库中,在test库中user表下新添加birthday
(bigint)字段:
在/src/main/java/quick
目录下创建mapper
文件夹,在其中创建接口UserMapper
:
1 2 3 4 public interface UserMapper { public void save (User user) ; public User findById (int id) ; }
编写UserMapper.xml
配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 <?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 ="quick.mapper.UserMapper" > <insert id ="save" parameterType ="user" > insert into user values(#{id}, #{username}, #{password}, #{birthday}) </insert > <select id ="findById" parameterType ="int" resultType ="user" > select * from user where id=#{id} </select > </mapper >
测试环境搭建完成。
具体的实现步骤:
定义转换类继承类BaseTypeHandler<T>
覆盖4个未实现的方法,其中setNonNullParameter
为java程序设置数据到数据库的回调方法,getNullableResult
为查询时mysql的字符串类型转换成java的Type类型的方法
在MyBatis核心配置文件中进行注册
测试转换是否正确
在/src/main/java/quick
目录下新建一个handler
文件夹,在其中编写DateTypeHandler
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import org.apache.ibatis.type.BaseTypeHandler;import org.apache.ibatis.type.JdbcType;import java.sql.CallableStatement;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.util.Date;public class DateTypeHandler extends BaseTypeHandler <Date > { public void setNonNullParameter (PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException { long time = date.getTime(); preparedStatement.setLong(i,time); } public Date getNullableResult (ResultSet resultSet, String s) throws SQLException { long aLong = resultSet.getLong(s); Date date = new Date(aLong); return date; } public Date getNullableResult (ResultSet resultSet, int i) throws SQLException { long aLong = resultSet.getLong(i); Date date = new Date(aLong); return date; } public Date getNullableResult (CallableStatement callableStatement, int i) throws SQLException { long aLong = callableStatement.getLong(i); Date date = new Date(aLong); return date; } }
在sqlMapConfig.xml
配置文件中注册类型处理器:
1 2 3 4 <typeHandlers > <typeHandler handler ="quick.handler.DateTypeHandler" > </typeHandler > </typeHandlers >
编写MyBatisTest
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class MyBatisTest { @Test public void test6 () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = new User(); user.setUsername("usertest" ); user.setPassword("abc" ); user.setBirthday(new Date()); mapper.save(user); sqlSession.commit(); sqlSession.close(); } @Test public void test7 () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.findById(8 ); System.out.println(user); sqlSession.commit(); sqlSession.close(); } }
测试,执行test6()
测试方法,刷新数据库,可以看到java的Date类型数据转换为毫秒数存储起来:
测试,执行test7()
测试方法,在控制台打印输出java的Date类型数据:
plugins标签
MyBatis可以使用第三方的插件来对功能进行扩展,只需要导入相应的坐标。
这里以分页助手PageHelper
为例:它可以是将分页的复杂操作进行封装,使用简单的方式即 可获得分页的相关数据
开发步骤:
导入通用PageHelper的坐标
在mybatis核心配置文件中配置PageHelper插件
测试分页数据获取
在接口UserMapper中新增findAll()
方法:
1 2 3 4 5 public interface UserMapper { public void save (User user) ; public User findById (int id) ; public List<User> findAll () ; }
在UserMapper.xml
中进行配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?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 ="quick.mapper.UserMapper" > <insert id ="save" parameterType ="user" > insert into user values(#{id}, #{username}, #{password}, #{birthday}) </insert > <select id ="findById" parameterType ="int" resultType ="user" > select * from user where id=#{id} </select > <select id ="findAll" resultType ="user" > select * from user </select > </mapper >
在pom.xml
配置文件中导入分页插件PageHelper的坐标:
1 2 3 4 5 6 7 8 9 10 <dependency > <groupId > com.github.pagehelper</groupId > <artifactId > pagehelper</artifactId > <version > 3.7.5</version > </dependency > <dependency > <groupId > com.github.jsqlparser</groupId > <artifactId > jsqlparser</artifactId > <version > 0.9.1</version > </dependency >
在sqlMapConfig.xml
配置文件中配置分页助手插件:
1 2 3 4 5 6 <plugins > <plugin interceptor ="com.github.pagehelper.PageHelper" > <property name ="dialect" value ="mysql" > </property > </plugin > </plugins >
注意:如果在pom.xml中导入的坐标版本是5.x.x,则这里不需要配置<property name="dialect" value="mysql"></property>
编写MyBatisTest测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class MyBatisTest { @Test public void test8 () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); PageHelper.startPage(1 ,3 ); List<User> userList = mapper.findAll(); for (User user : userList) { System.out.println(user); } sqlSession.commit(); sqlSession.close(); } }
数据库数据如下:
这里设置查询第二页(每页显示三条数据)
运行测试,结果如下:
也可以获取分页相关的数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class MyBatisTest { @Test public void test8 () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); PageHelper.startPage(1 ,3 ); List<User> userList = mapper.findAll(); for (User user : userList) { System.out.println(user); } PageInfo<User> pageInfo = new PageInfo<User>(userList); 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.isIsFirstPage()); System.out.println("是否是末页:" + pageInfo.isIsLastPage()); sqlSession.commit(); sqlSession.close(); } }
执行测试,打印结果如下:
常用API
工厂构建器SqlSessionFactoryBuilder
常用API:SqlSessionFactory build(InputStream inputStream)
通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象:
1 2 3 4 String resource = "org/mybatis/builder/mybatis-config.xml" ; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(inputStream);
其中, Resources工具类,这个类在org.apache.ibatis.io包中。Resources类帮助从类路径下、文件系统或 一个 web URL中加载资源文件。
工厂对象SqlSessionFactory
SqlSessionFactory有多个方法创建SqlSession实例。常用的有如下两个:
方法
解释
openSession()
会默认开启一个事务,但事务不会自动提交,也就意味着需要手动提 交该事务,更新操作数据才会持久化到数据库中
openSession(boolean autoCommit)
参数为是否自动提交,如果设置为true,那么不需要手动提交事务
会话对象
SqlSession实例在MyBatis中是非常强大的一个类。在这里会看到所有执行语句、提交或回滚事务和获取映射器实例的方法。
执行语句的方法主要有:
1 2 3 4 5 <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)
操作事务的方法主要有:
1 2 void commit () void rollback ()
MyBatis的Dao层实现
传统开发方式
在/src/main/java/quick
目录下创建dao
文件夹,在其中编写UserMapper
接口以及对应的实现类UserMapperImpl
:
UserMapper
接口:
1 2 3 4 5 6 import quick.domain.User;import java.io.IOException;import java.util.List;public interface UserMapper { public List<User> findAll () throws IOException ; }
UserMapperImpl
实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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 quick.dao.UserMapper;import quick.domain.User;import java.io.IOException;import java.io.InputStream;import java.util.List;public class UserMapperImpl implements UserMapper { public List<User> findAll () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); List<User> userList = sqlSession.selectList("userMapper.findAll" ); return userList; } }
在/src/main/java/quick
目录下创建service
文件夹,在其中创建ServiceDemo
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import quick.dao.UserMapper;import quick.dao.impl.UserMapperImpl;import quick.domain.User;import java.io.IOException;import java.util.List;public class ServiceDemo { public static void main (String[] args) throws IOException { UserMapper userMapper = new UserMapperImpl(); List<User> all = userMapper.findAll(); System.out.println(all); } }
手动创建dao层对象,执行,控制台打印查询结果:
代理开发方式
采用Mybatis的代理开发方式实现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
的类型相同
为了方便对比,删除/src/main/java/quick/dao/impl
目录及其内部的实现类。
编写接口UserMapper
:
1 2 3 4 5 6 7 8 9 10 11 import quick.domain.User;import java.io.IOException;import java.util.List;public interface UserMapper { public List<User> findAll () throws IOException ; public User findById (int id) ; }
编写UserMapper.xml
配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 <?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 ="quick.dao.UserMapper" > <select id ="findAll" resultType ="user" > select * from user </select > <select id ="findById" parameterType ="int" resultType ="user" > select * from user where id=#{id} </select > </mapper >
注意:resultType="user"
在sqlMapConfig.xml
中配置过别名
编写ServiceDemo
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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 quick.dao.UserMapper;import quick.domain.User;import java.io.IOException;import java.io.InputStream;import java.util.List;public class ServiceDemo { public static void main (String[] args) throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> all = mapper.findAll(); System.out.println(all); User user = mapper.findById(1 ); System.out.println(user); } }
执行,控制台打印查询结果:
映射文件
动态sql语句
有些时候业务逻辑复杂时,SQL是动态变化的。
if 标签
根据实体类的不同取值,使用不同的SQL语句来进行查询。
在/src/main/java/quick
目录下创建mapper
文件夹,在其中创建接口UserMapper
:
1 2 3 4 5 6 import quick.domain.User;import java.util.List;public interface UserMapper { public List<User> findByCondition (User user) ; }
编写UserMapper.xml
配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?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 ="quick.mapper.UserMapper" > <select id ="findByCondition" parameterType ="user" resultType ="user" > select * from user <where > <if test ="id!=0" > and id=#{id} </if > <if test ="username!=null" > and username=#{username} </if > <if test ="password!=null" > and password=#{password} </if > </where > </select > </mapper >
在/src/test/java/test
目录下编写MapperTest
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 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 quick.mapper.UserMapper;import quick.domain.User;import java.io.IOException;import java.io.InputStream;import java.util.List;public class MapperTest { @Test public void test1 () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User condition1 = new User(); condition1.setId(1 ); condition1.setUsername("ZhangSan" ); condition1.setPassword("123" ); List<User> userList1 = mapper.findByCondition(condition1); System.out.println(userList1); User condition2 = new User(); condition2.setId(1 ); List<User> userList2 = mapper.findByCondition(condition2); System.out.println(userList2); User condition3 = new User(); condition3.setUsername("ZhangSan" ); List<User> userList3 = mapper.findByCondition(condition3); System.out.println(userList3); User condition4 = new User(); List<User> userList4 = mapper.findByCondition(condition4); System.out.println(userList4); } }
这里模拟了四种条件的User对象,配置文件会根据不同的条件执行不同的SQL语句
运行测试,结果如下:
foreach标签
循环执行sql的拼接操作。(例如:SELECT * FROM USER WHERE id IN (1,2,5)
)
编写UserMapper
接口:
1 2 3 4 5 6 import quick.domain.User;import java.util.List;public interface UserMapper { public List<User> findByIds (List<Integer> ids) ; }
编写UserMapper.xml
配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?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 ="quick.mapper.UserMapper" > <select id ="findByIds" parameterType ="list" resultType ="user" > select * from user <where > <foreach collection ="list" open ="id in(" close =")" item ="id" separator ="," > #{id} </foreach > </where > </select > </mapper >
说明:
collection:代表要遍历的集合元素,注意编写时不要写#{}
open:代表语句的开始部分
close:代表结束部分
item:代表遍历集合的每个元素,生成的变量名
sperator:代表分隔符
编写MapperTest
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class MapperTest { @Test public void test2 () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<Integer> ids = new ArrayList<Integer>(); ids.add(1 ); ids.add(2 ); List<User> userList = mapper.findByIds(ids); System.out.println(userList); } }
运行测试,结果如下:
SQL片段抽取
Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的
编写UserMapper.xml
配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <?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 ="quick.mapper.UserMapper" > <sql id ="selectUser" > select * from user</sql > <select id ="findByCondition" parameterType ="user" resultType ="user" > <include refid ="selectUser" > </include > <where > <if test ="id!=0" > and id=#{id} </if > <if test ="username!=null" > and username=#{username} </if > <if test ="password!=null" > and password=#{password} </if > </where > </select > <select id ="findByIds" parameterType ="list" resultType ="user" > <include refid ="selectUser" > </include > <where > <foreach collection ="list" open ="id in(" close =")" item ="id" separator ="," > #{id} </foreach > </where > </select > </mapper >
多表操作
环境搭建:
新建一个Maven项目
pom.xml
配置文件导入坐标:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <dependencies > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.21</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.4.6</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > <scope > test</scope > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency > </dependencies >
创建目录/src/main/java/multi/domain
,在其中编写User
类和Order
类。
User
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class User { private int id; private String username; private String password; private Date birthday; public Date getBirthday () { return birthday; } public void setBirthday (Date birthday) { this .birthday = birthday; } public int getId () { return id; } public void setId (int 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; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", birthday=" + birthday + '}' ; } }
Order
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class Order { private int id; private Date ordertime; private double total; private User user; public int getId () { return id; } public void setId (int id) { this .id = id; } public Date getOrdertime () { return ordertime; } public void setOrdertime (Date ordertime) { this .ordertime = ordertime; } public double getTotal () { return total; } public void setTotal (double total) { this .total = total; } public User getUser () { return user; } public void setUser (User user) { this .user = user; } @Override public String toString () { return "Order{" + "id=" + id + ", ordertime=" + ordertime + ", total=" + total + ", user=" + user + '}' ; } }
创建目录/src/main/java/multi/mapper
,在其中创建接口UserMapper
和接口OrderMapper
。
接口UserMapper
:
1 2 public interface UserMapper {}
接口OrderMapper
:
1 2 public interface OrderMapper {}
创建目录/src/resources/multi/mapper
,在其中编写配置文件UserMapper.xml
和配置文件OrderMapper.xml
。
配置文件UserMapper.xml
:
1 2 3 4 5 <?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 ="multi.mapper.UserMapper" > </mapper >
配置文件OrderMapper.xml
:
1 2 3 4 5 <?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 ="multi.mapper.OrderMapper" > </mapper >
在/src/resources
目录下编写jdbc.properties
配置文件:
1 2 3 4 jdbc.driver =com.mysql.cj.jdbc.Driver jdbc.url =jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.username =root jdbc.password =root
在/src/resources
目录下编写sqlMapConfig.xml
配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?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 > <typeAliases > <typeAlias type ="multi.domain.User" alias ="user" > </typeAlias > <typeAlias type ="multi.domain.Order" alias ="order" > </typeAlias > </typeAliases > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <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 ="multi/mapper/UserMapper.xml" > </mapper > <mapper resource ="multi/mapper/OrderMapper.xml" > </mapper > </mappers > </configuration >
在MySQL数据库中的test库中,新建:
user
表,分别创建id
(int,主键)、username
(varchar)、password
(varchar)、birthday
(datetime)字段,并添加数据:
orders
表,分别创建id
(int,主键)、ordertime
(varchar)、total
(double)、uid
(int)字段,并添加数据:
创建目录/src/test/java/multi/test
在其中创建测试类MybatisTest
:
1 2 public class MybatisTest {}
一对一查询
用户表和订单表的关系为:一个用户有多个订单,一个订单只从属于一个用户
需求:查询一个订单,与此同时查询出该订单所属的用户
查询语句:select *,o.id oid from orders o,user u where o.uid=u.id
在OrderMapper
接口中编写查询方法:
1 2 3 4 public interface OrderMapper { public List<Order> findAll () ; }
编写OrderMapper.xml
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?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 ="multi.mapper.OrderMapper" > <resultMap id ="orderMap" type ="order" > <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 > </resultMap > <select id ="findAll" resultMap ="orderMap" > select *,o.id oid from orders o,user u where o.uid=u.id </select > </mapper >
编写测试类MybatisTest
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class MybatisTest { @Test public void test1 () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); OrderMapper mapper = sqlSession.getMapper(OrderMapper.class); List<Order> orderList = mapper.findAll(); for (Order order : orderList) { System.out.println(order); } sqlSession.close(); } }
还可以利用<association>
标签将order实体中的user属性单独拆开写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?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 ="multi.mapper.OrderMapper" > <resultMap id ="orderMap" type ="order" > <id column ="oid" property ="id" > </id > <result column ="ordertime" property ="ordertime" > </result > <result column ="total" property ="total" > </result > <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 > </mapper >
执行测试,看以看到控制台输出所有订单信息(用户所属):
一对多查询
用户表和订单表的关系为:一个用户有多个订单,一个订单只从属于一个用户
需求:查询一个用户,与此同时查询出该用户具有的订单
查询语句:select *,o.id oid from user u, orders o where u.id=o.uid
编写User
类,添加orderList属性及其getter、setter方法,并重写toString()
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public class User { private int id; private String username; private String password; private Date birthday; private List<Order> orderList; public List<Order> getOrderList () { return orderList; } public void setOrderList (List<Order> orderList) { this .orderList = orderList; } public Date getBirthday () { return birthday; } public void setBirthday (Date birthday) { this .birthday = birthday; } public int getId () { return id; } public void setId (int 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; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", birthday=" + birthday + ", orderList=" + orderList + '}' ; } }
编写UserMapper
接口的方法:
1 2 3 public interface UserMapper { public List<User> findAll () ; }
编写UserMapper.xml
配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <?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 ="multi.mapper.UserMapper" > <resultMap id ="userMap" type ="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 > <collection property ="orderList" ofType ="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.uid </select > </mapper >
编写MybatisTest
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class MybatisTest { @Test public void test2 () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.findAll(); for (User user : userList) { System.out.println(user); } sqlSession.close(); } }
执行测试,可以看到输出用户的所有订单信息:
多对多查询
在MySQL数据库中的test库中,新建:
role
表,分别创建id
(int,主键)、roleName
(varchar)、roleDesc
(varchar)字段,并添加数据:
user_role
表,分别创建user_id
(int)、role_id
(int)字段,并添加数据:
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
需求:查询用户同时查询出该用户的所有角色
查询语句:select * from user u,user_role ur,role r where u.id=ur.user_id and ur.role_id=r.id
在/src/main/java/multi/domain
目录下新建Role
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class Role { private int id; private String roleName; private String roleDesc; public int getId () { return id; } public void setId (int id) { this .id = id; } public String getRoleName () { return roleName; } public void setRoleName (String roleName) { this .roleName = roleName; } public String getRoleDesc () { return roleDesc; } public void setRoleDesc (String roleDesc) { this .roleDesc = roleDesc; } @Override public String toString () { return "Role{" + "id=" + id + ", roleName='" + roleName + '\'' + ", roleDesc='" + roleDesc + '\'' + '}' ; } }
编写User
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 public class User { private int id; private String username; private String password; private Date birthday; private List<Order> orderList; private List<Role> roleList; public List<Role> getRoleList () { return roleList; } public void setRoleList (List<Role> roleList) { this .roleList = roleList; } public List<Order> getOrderList () { return orderList; } public void setOrderList (List<Order> orderList) { this .orderList = orderList; } public Date getBirthday () { return birthday; } public void setBirthday (Date birthday) { this .birthday = birthday; } public int getId () { return id; } public void setId (int 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; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", birthday=" + birthday + ", orderList=" + orderList + ", roleList=" + roleList + '}' ; } }
在UserMapper接口中添加方法:
1 2 3 4 public interface UserMapper { public List<User> findAll () ; public List<User> findUserAndRoleAll () ; }
在sqlMapConfig.xml配置文件中配置Role的别名:
1 2 3 4 5 6 <typeAliases > <typeAlias type ="multi.domain.User" alias ="user" > </typeAlias > <typeAlias type ="multi.domain.Order" alias ="order" > </typeAlias > <typeAlias type ="multi.domain.Role" alias ="role" > </typeAlias > </typeAliases >
编写UserMapper.xml
配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?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 ="multi.mapper.UserMapper" > <resultMap id ="userRoleMap" type ="user" > <id column ="user_id" 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 ="roleName" property ="roleName" > </result > <result column ="roleDesc" property ="roleDesc" > </result > </collection > </resultMap > <select id ="findUserAndRoleAll" resultMap ="userRoleMap" > select * from user u,user_role ur,role r where u.id=ur.user_id and ur.role_id=r.id </select > </mapper >
编写MybatisTest测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class MybatisTest { @Test public void test3 () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userAndRoleAll = mapper.findUserAndRoleAll(); for (User user : userAndRoleAll) { System.out.println(user); } sqlSession.close(); } }
运行测试,可以看到控制台打印输出User对应的角色描述:
注解开发
注解
描述
@Insert
实现新增
@Update
实现更新
@Delete
实现删除
@Select
实现查询
@Result
实现结果集封装
@Results
可以与@Result 一起使用,封装多个结果集
@One
可以与@Result 一起使用,封装多个结果集
@Many
实现一对多结果集封装
环境搭建:
新建Maven项目:
配置pom.xml依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <dependencies > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.21</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.4.6</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > <scope > test</scope > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.10</version > <scope > test</scope > </dependency > </dependencies >
新建目录/src/main/java/anno/domain
,在其中编写User
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class User { private int id; private String username; private String password; private Date birthday; public int getId () { return id; } public void setId (int 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 Date getBirthday () { return birthday; } public void setBirthday (Date birthday) { this .birthday = birthday; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", birthday=" + birthday + '}' ; } }
新建目录/src/main/java/anno/mapper
,在其中编写接口UserMapper
:
1 2 3 4 5 6 7 public interface UserMapper { public void save (User user) ; public void update (User user) ; public void delete (int id) ; public User findById (int id) ; public List<User> findAll () ; }
新建目录/src/main/resources/anno/mapper
,在其中编写UserMapper.xml
配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?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 ="anno.mapper.UserMapper" > <insert id ="save" parameterType ="user" > insert into user values(#{id},#{username},#{password},#{birthday}) </insert > <update id ="update" parameterType ="user" > update user set username=#{username},password=#{password} where id=#{id} </update > <delete id ="delete" parameterType ="int" > delete from user where id=#{id} </delete > <select id ="findById" parameterType ="int" resultType ="user" > select * from user where id=#{id} </select > <select id ="findAll" resultType ="user" > select * from user </select > </mapper >
在/src/main/resources
目录下配置jdbc.properties
:
1 2 3 4 jdbc.driver =com.mysql.cj.jdbc.Driver jdbc.url =jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.username =root jdbc.password =root
在/src/main/resources
目录下编写sqlMapConfig.xml
配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?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 > <typeAliases > <typeAlias type ="anno.domain.User" alias ="user" > </typeAlias > </typeAliases > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" > </transactionManager > <dataSource type ="POOLED" > <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 ="anno/mapper/UserMapper.xml" > </mapper > </mappers > </configuration >
创建目录/src/test/java/anno/test
,在其中编写测试类MyBatisTest
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 import anno.domain.User;import anno.mapper.UserMapper;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.Before;import org.junit.Test;import java.io.IOException;import java.io.InputStream;import java.util.List;public class MyBatisTest { private UserMapper mapper; @Before public void before () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(true ); mapper = sqlSession.getMapper(UserMapper.class); } @Test public void testSave () { User user = new User(); user.setUsername("tom" ); user.setPassword("abc" ); mapper.save(user); } @Test public void testUpdate () { User user = new User(); user.setId(10 ); user.setUsername("lucy" ); user.setPassword("123" ); mapper.update(user); } @Test public void testDelete () { mapper.delete(10 ); } @Test public void testFindById () { User user = mapper.findById(1 ); System.out.println(user); } @Test public void testFindAll () { List<User> userList = mapper.findAll(); for (User user : userList) { System.out.println(user); } } }
执行testSave()
方法,刷新数据库,可以看到添加tom用户的信息:
执行testUpdate()
方法,刷新数据库,可以看到用户tom的信息已修改为lucy:
执行testDelete()
方法,刷新数据库,删除用户lucy(id=10)成功:
执行testFindById()
方法,可以看到控制台打印id=1的用户信息:
执行testFindAll()
方法,可以看到控制台打印所有用户信息:
增删改查
删除目录/src/main/resources/anno
及其子目录
注意:必须删除UserMapper.xml
配置文件,否则后面会报如下错误:
后面引入注解扫描时会识别错误,误识别原有的xml配置
修改UserMapper
接口,使用注解的方式添加sql语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public interface UserMapper { @Insert("insert into user values(#{id},#{username},#{password},#{birthday})") public void save (User user) ; @Update("update user set username=#{username},password=#{password} where id=#{id}") public void update (User user) ; @Delete("delete from user where id=#{id}") public void delete (int id) ; @Select("select * from user where id=#{id}") public User findById (int id) ; @Select("select * from user") public List<User> findAll () ; }
在sqlMapConfig.xml
配置文件中重新加载映射关系,使其扫描指定包下的注解:
1 2 3 4 5 <mappers > <package name ="anno.mapper" > </package > </mappers >
最后执行测试,效果一样。(这里不做展示)
复杂映射
之前实现复杂关系映射可以在映射文件中通过配置<resultMap>
来实现。
使用注解开发后,可以使用@Results
注解 ,@Result
注解,@One
注解,@Many
注解组合完成复杂关系的配置。
@Results
代替的是标签<resultMap>
该注解中可以使用单个@Result注解,也可以使用@Result集合
使用格式:@Results({@Result(),@Result()})
或 @Results(@Result())
@Resut
代替了<id>
标签和<result>
标签
@Result中属性介绍:
column:数据库的列名
property:需要装配的属性名
one:需要使用的@One注解(
@Result(one=@One)()`)
many:需要使用的@Many
注解(@Result(many=@many)()
)
@One
(一对一)
@Many
(多对一)
代替了<collection>
标签,是多表查询的关键,在注解中用来指定子查询返回对象集合
使用格式:@Result(property="",column="",many=@Many(select=""))
一对一查询
需求:查询一个订单,与此同时查询出该订单所属的用户
在目录/src/main/java/multi/domain
中编写Order
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class Order { private int id; private Date ordertime; private double total; private User user; public int getId () { return id; } public void setId (int id) { this .id = id; } public Date getOrdertime () { return ordertime; } public void setOrdertime (Date ordertime) { this .ordertime = ordertime; } public double getTotal () { return total; } public void setTotal (double total) { this .total = total; } public User getUser () { return user; } public void setUser (User user) { this .user = user; } @Override public String toString () { return "Order{" + "id=" + id + ", ordertime=" + ordertime + ", total=" + total + ", user=" + user + '}' ; } }
在目录/src/main/java/multi/mapper
中编写接口OrderMapper
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public interface OrderMapper { @Select("select *,o.id oid from orders o,user u where o.uid=u.id ") @Results({ @Result(column = "oid",property = "id"), @Result(column = "ordertime",property = "ordertime"), @Result(column = "total",property = "total"), @Result(column = "uid",property = "user.id"), @Result(column = "username",property = "user.username"), @Result(column = "password",property = "user.password"), @Result(column = "birthday",property = "user.birthday") }) public List<Order> findAll () ; }
也可以采用如下方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public interface OrderMapper { @Select("select * from orders") @Results({ @Result(column = "id",property = "id"), @Result(column = "ordertime",property = "ordertime"), @Result(column = "total",property = "total"), @Result( property = "user", //要封装的属性名称 column = "uid", //根据哪个字段去查询user表的数据 javaType = User.class, //要封装的实体类型 //select属性:代表查询哪个接口的方法获得数据 one = @One(select = "anno.mapper.UserMapper.findById") ) }) public List<Order> findAll () ; }
在/src/test/java/anno/test
目录下编写MyBatisTest2
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class MyBatisTest2 { private OrderMapper mapper; @Before public void before () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(true ); mapper = sqlSession.getMapper(OrderMapper.class); } @Test public void test () { List<Order> orderList = mapper.findAll(); for (Order order : orderList) { System.out.println(order); } } }
执行测试,结果如下:
一对多查询
需求:查询一个用户,与此同时查询出该用户具有的订单
修改User
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public class User { private int id; private String username; private String password; private Date birthday; private List<Order> orderList; public List<Order> getOrderList () { return orderList; } public void setOrderList (List<Order> orderList) { this .orderList = orderList; } public int getId () { return id; } public void setId (int 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 Date getBirthday () { return birthday; } public void setBirthday (Date birthday) { this .birthday = birthday; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", birthday=" + birthday + ", orderList=" + orderList + '}' ; } }
编写接口UserMapper
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public interface UserMapper { @Select("select * from user") @Results({ @Result(id=true ,column = "id",property = "id"), @Result(column = "username",property = "username"), @Result(column = "password",property = "password"), @Result( property = "orderList", column = "id", javaType = List.class, many = @Many(select = "anno.mapper.OrderMapper.findByUid") ) }) public List<User> findUserAndOrderAll () ; }
编写接口OrderMapper
:
1 2 3 4 5 6 public interface OrderMapper { @Select("select * from orders where uid=#{uid}") public List<Order> findByUid (int uid) ; }
在/src/test/java/anno/test
目录下编写MyBatisTest3
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class MyBatisTest3 { private UserMapper mapper; @Before public void before () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(true ); mapper = sqlSession.getMapper(UserMapper.class); } @Test public void test () { List<User> userAndOrderAll = mapper.findUserAndOrderAll(); for (User user : userAndOrderAll) { System.out.println(user); } } }
执行测试,结果如下:
多对多查询
需求:查询用户同时查询出该用户的所有角色
修改User
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 public class User { private int id; private String username; private String password; private Date birthday; private List<Role> roleList; private List<Order> orderList; public List<Order> getOrderList () { return orderList; } public List<Role> getRoleList () { return roleList; } public void setRoleList (List<Role> roleList) { this .roleList = roleList; } public void setOrderList (List<Order> orderList) { this .orderList = orderList; } public int getId () { return id; } public void setId (int 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 Date getBirthday () { return birthday; } public void setBirthday (Date birthday) { this .birthday = birthday; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", birthday=" + birthday + ", roleList=" + roleList + ", orderList=" + orderList + '}' ; } }
在目录/src/main/java/multi/domain
中编写Role
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class Role { private int id; private String roleName; private String roleDesc; public int getId () { return id; } public void setId (int id) { this .id = id; } public String getRoleName () { return roleName; } public void setRoleName (String roleName) { this .roleName = roleName; } public String getRoleDesc () { return roleDesc; } public void setRoleDesc (String roleDesc) { this .roleDesc = roleDesc; } @Override public String toString () { return "Role{" + "id=" + id + ", roleName='" + roleName + '\'' + ", roleDesc='" + roleDesc + '\'' + '}' ; } }
在目录/src/main/java/multi/mapper
中编写接口RoleMapper
:
1 2 3 4 5 6 public interface RoleMapper { @Select("select * from user_role ur,role r where ur.role_id=r.id and ur.user_id=#{uid}") public List<Role> findByUid (int uid) ; }
编写接口UserMapper
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public interface UserMapper { @Select("select * from user") @Results({ @Result(id = true,column = "id",property = "id"), @Result(id = true,column = "username",property = "username"), @Result(id = true,column = "password",property = "password"), @Result(id = true,column = "birthday",property = "birthday"), @Result( property = "roleList", column = "id", javaType = List.class, many = @Many(select = "anno.mapper.RoleMapper.findByUid") ) }) public List<User> findUserAndRoleAll () ; }
在/src/test/java/anno/test
目录下编写MyBatisTest4
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class MyBatisTest4 { private UserMapper mapper; @Before public void before () throws IOException { InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); SqlSession sqlSession = sqlSessionFactory.openSession(true ); mapper = sqlSession.getMapper(UserMapper.class); } @Test public void test () { List<User> userAndRoleAll = mapper.findUserAndRoleAll(); for (User user : userAndRoleAll) { System.out.println(user); } } }
执行测试,结果如下:
后记
个人感觉如果sql语句复杂,还是用xml配置文件的方式便捷,注解的可读性差一些。