文章目录

  • 一.简介
  • 二.快速入门
  • 三.映射文件配置详解
    • 1.基本增删改查标签、属性
    • 2.动态sql
    • 2.1\标签
    • 2.2\标签
  • 四.核心配置文件
    • 1.核心配置文件标签层级关系及作用
    • 2.配置示例
    • 3.事务管理器
    • 4.数据源
    • 5.Mapper标签
    • 6.自定义类型转换器
    • 7.插件机制
  • 五.相应API
    • 1.工具对象
    • 2.openSession方法
    • 3.SqlSession会话对象
  • 六.Mybatis的Dao层实现方式
    • 1.代理开发方式介绍
  • 七.多表操作
    • 1.一对一、一对多查询
    • 2.多对多查询
    • 3.总结
  • 八.Mybatis注解开发
    • 1.注解扫描
    • 2.注解详解

一.简介

Mybatis是一个基于java的持久层框架,它主要用来解决原生JDBC代码冗余、重复,频繁申请释放连接资源等问题,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。并采用ORM思想,可以自动对Bean对象进行映射封装。此外,它还可以将Sql语句配置到xml文件中,实现持久操作与编码的松耦合。

总之,Mybatis可以让我们通过配置的方式与数据库进行交互,完成数据的持久化等操作

二.快速入门

这里我们先做一个快速入门,后面我们会对具体配置流程进行剖析详解。

下面以一个查询用户表的案例进行快速入门。

① 添加MyBatis的坐标

<!--mybatis坐标-->
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.5</version></dependency>
<!--mysql驱动坐标-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version><scope>runtime</scope>
</dependency>

② 创建数据库中的user数据表

③ 编写User实体类

创建了一个实体POJO,包含如下两个属性

private String username;
private String password;

④ 编写映射文件UserMapper.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="user"><select id="findAll" resultType="POJO.User">select * from user</select>
</mapper>

⑤ 编写核心文件MybatisConfig.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><environments default="dev"><environment id="dev"><transactionManager type="JDBC"></transactionManager><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql:///jdbc"></property><property name="username" value="root"></property><property name="password" value="root"></property></dataSource></environment></environments><mappers><mapper resource="mapper/UserMapper.xml"></mapper></mappers>
</configuration>

⑥ 编写测试类

public class MybatisTest {@Testpublic void test1() throws IOException {//使用Mybatis提供的Resources对象加载核心配置文件InputStream config = Resources.getResourceAsStream("MybatisConfig.xml");//获取sql会话工厂SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);//从sql会话工厂获取一个sql会话对象SqlSession sqlSession = sqlSessionFactory.openSession();//执行查询语句,返回结果集List<User> userList = sqlSession.selectList("user.findAll");//打印结果System.out.println(userList);//归还连接sqlSession.close();}
}

运行之后,可以正常输出我们数据表中的数据

[User{name=‘123’, password=‘123’}, User{name=‘aaa’, password=‘bbb’}]

三.映射文件配置详解

1.基本增删改查标签、属性

映射文件约束头使用dtd类型约束头,以下映射文件中配置了对user表进行增删改查操作

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="user"><select id="findAll" resultType="POJO.User">select * from user</select><insert id="add" parameterType="POJO.User">insert into user values(#{name},#{password})</insert><delete id="del" parameterType="String">delete from user where name = #{name}</delete><update id="update" parameterType="POJO.User">update user set password = #{password} where name = #{name}</update>
</mapper>
@Test
public void testAdd() throws IOException {//模拟一个user对象用于插入User wangwu = new User("wangwu", "123456");InputStream config = Resources.getResourceAsStream("MybatisConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);SqlSession sqlSession = sqlSessionFactory.openSession();int change = sqlSession.insert("user.add",wangwu);System.out.println(change);//提交事务sqlSession.commit();sqlSession.close();
}@Test
public void testDel() throws IOException {InputStream config = Resources.getResourceAsStream("MybatisConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);SqlSession sqlSession = sqlSessionFactory.openSession();int change = sqlSession.insert("user.del","123");System.out.println(change);//提交事务sqlSession.commit();sqlSession.close();
}@Test
public void testUpdate() throws IOException {//模拟一个user对象用于更新User wangwu = new User("wangwu", "987654");InputStream config = Resources.getResourceAsStream("MybatisConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);SqlSession sqlSession = sqlSessionFactory.openSession();int change = sqlSession.insert("user.update",wangwu);System.out.println(change);//提交事务sqlSession.commit();sqlSession.close();
}

我们配置的事务管理器是JDBC,对于增删改操作默认不会自动提交,所以我们需要手动提交。

以下是标签及属性的具体作用说明。

注意:

  • Mapper命名空间不为空
  • 占位符使用**#{实体属性名}**的格式
  • 指定resultType用于自动映射封装查询结果对象,如果返回值是集合,则此处应配集合所存储的数据类型

2.动态sql

Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的,有些时候业务逻辑复杂时,我们的 SQL是动态变化的, 此时在前面的学习中我们的 SQL 就不能满足要求了。 试想一下,我们在进行多条件查询的时候,我们经常要对字符串进行拼接,类似下面这样

显然,这一过程很繁琐枯燥,而且容易出错,动态Sql的出现就是为了帮我们解决类似的问题。

Mybatis的提供的动态Sql标签如下:

  • if
  • foreach

2.1<if>标签

下面我们进行测试,首先为User类型再添加一个属性id

现在User类有三个属性:id,user,password

配置映射文件如下,如果id不为空,就可以通过id查;如果name不为空,还要加入name作为查询条件。

<select id="findOne" resultType="POJO.User" parameterType="POJO.User">select * from user<where><if test="id!=0">and id = #{id}</if><if test="name!=null">and name = #{name}</if></where>
</select>

只有name,id为空的情况下:

public void testFindOne() throws IOException {//模拟一个user对象用于查询User wangwu = new User();wangwu.setName("wangwu");InputStream config = Resources.getResourceAsStream("MybatisConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);SqlSession sqlSession = sqlSessionFactory.openSession();List<User> userList = sqlSession.selectList("user.findOne",wangwu);System.out.println(userList);sqlSession.close();
}

查看日志信息,预编译sql语句的条件字段只有name

18:11:27,969 DEBUG findOne:159 - ==> Preparing: select * from user WHERE name = ?
18:11:28,001 DEBUG findOne:159 - > Parameters: wangwu(String)
18:11:28,022 DEBUG findOne:159 - < Total: 1
[User{id=2, name=‘wangwu’, password=‘987654’}]

name和id都存在的情况下

public void testFindOne() throws IOException {//模拟一个user对象用于查询User wangwu = new User();wangwu.setName("wangwu");wangwu.setId(2);InputStream config = Resources.getResourceAsStream("MybatisConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);SqlSession sqlSession = sqlSessionFactory.openSession();List<User> userList = sqlSession.selectList("user.findOne",wangwu);System.out.println(userList);sqlSession.close();
}

查看日志信息,结果如下,可以发现预编译语句id和name字段都存在了

18:15:55,642 DEBUG findOne:159 - ==> Preparing: select * from user WHERE id = ? and name = ?
18:15:55,672 DEBUG findOne:159 - > Parameters: 2(Integer), wangwu(String)
18:15:55,689 DEBUG findOne:159 - < Total: 1
[User{id=2, name=‘wangwu’, password=‘987654’}]

2.2<foreach>标签

其主要有以下几个属性

  • collection:代表要遍历的集合元素,注意编写时不要写#{}。参数集为数组则值为array,参数集为集合则值为list
  • open:代表语句的开始部分
  • close:代表结束部分
  • item:代表遍历数组(集合)的每个元素,为其临时指派的变量名
  • sperator:代表分隔符
<!--sql抽取-->
<sql id="selectall">select * from user</sql>
<select id="findSet" resultType="POJO.User" parameterType="Integer"><include refid="selectall"/><where><foreach collection="array" open="id in(" close=")" separator="," item="id">#{id}</foreach></where>
</select>
@Test
public void testFindSet() throws IOException {int ids[] = {4,5,6};InputStream config = Resources.getResourceAsStream("MybatisConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);SqlSession sqlSession = sqlSessionFactory.openSession();List<User> userList = sqlSession.selectList("user.findSet",ids);System.out.println(userList);sqlSession.close();
}

控制台输出信息如下:

18:29:14,210 DEBUG findSet:159 - ==> Preparing: select * from user WHERE id in( ? , ? , ? )
18:29:14,238 DEBUG findSet:159 - > Parameters: 4(Integer), 5(Integer), 6(Integer)
18:29:14,261 DEBUG findSet:159 - < Total: 3
[User{id=4, name=‘wangwu1’, password=‘123456’}, User{id=5, name=‘tianqi’, password=‘68989’}, User{id=6, name=‘zhaoliu’, password=‘68899’}]

以上两个标签都被包含在where标签里面。

四.核心配置文件

1.核心配置文件标签层级关系及作用

  • configuration跟标签

    • properties 该标签专门用来加载外部的properties文件,如数据库信息配置
    • settings设置
    • typeAliases类型别名,用于设置类型的别名,一些基本类型mybatis框架已经帮我们设置好了
    • typeHandlers类型处理器
    • plugins插件
    • environments环境配置集
      • environment环境

        • transactionManager 事务管理器
        • dataSource 数据源
    • mappers 映射器,用于引入映射文件
    • objectFactory对象工厂
    • databaseIdProvider数据库厂商标识

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">
<configuration><!--引入数据库配置文件--><properties resource="jdbc.properties"></properties><!--配置环境--><environments default="dev"><environment id="dev"><!--指定事务管理器--><transactionManager type="JDBC"></transactionManager><!--配置数据源--><dataSource type="POOLED"><property name="driver" value="${driver}"></property><property name="url" value="${url}"></property><property name="username" value="${username}"></property><property name="password" value="${password}"></property></dataSource></environment></environments><!--引入映射文件--><mappers><mapper resource="mapper/UserMapper.xml"></mapper></mappers>
</configuration>

核心文件必不可少的三要素

  • 数据源
  • 事务管理器
  • 加载映射文件

3.事务管理器

事务管理器(transactionManager)类型有两种:

  • JDBC:这个配置就是直接使用了JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
  • MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE 应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置 为 false 来阻止它默认的关闭行为。

4.数据源

数据源(dataSource)类型有三种:

  • UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
  • POOLED:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来。即使用了数据库连接池。
  • JNDI:这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置 一个 JNDI 上下文的引用。

5.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"/>

6.自定义类型转换器

typeHandlers标签用于配置自定义类型转换器。

当我们保存在数据库中的数据是一个long型的时间戳,而我们的业务需求是将其转换为Date类型,这时只需要实现 org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler,配置Mybatis自定义类型转换器即可

开发步骤:

①定义转换类继承类BaseTypeHandler<T>,T为Java的类型

②覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult 为查询时 mysql的字符串类型转换成 java的Type类型的方法

③在MyBatis核心配置文件中进行注册

我们现在定义一个日期类型转换器。第一个重写方法在数据写入到数据库时被Mybatis框架调用,后面的三个方法是从数据库查询数据时被调用的重载方法,他们的业务逻辑都是一样的。

public class DateTypeHandler extends BaseTypeHandler<Date> {@Overridepublic void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {//将日期转换为long型时间戳long time = date.getTime();//类型预编译语句设置字段的值preparedStatement.setLong(i,time);}@Overridepublic Date getNullableResult(ResultSet resultSet, String s) throws SQLException {long aLong = resultSet.getLong(s);Date date = new Date(aLong);return date;}@Overridepublic Date getNullableResult(ResultSet resultSet, int i) throws SQLException {//从数据库中获取long型时间戳long aLong = resultSet.getLong(i);//将long型时间戳转换为Date行Date date = new Date(aLong);return date;}@Overridepublic Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {long aLong = callableStatement.getLong(i);Date date = new Date(aLong);return date;}
}
方法 参数 说明
setNonNullParameter preparedStatement 当前执行的sql预编译语句
setNonNullParameter i 代表表中的字段的索引
getNullableResult resultSet 返回结果集
getNullableResult s 字段名称
getNullableResult i 代表表中的字段的索引

在配置文件中声明类型转换器,配置全包名即可

<typeHandlers><typeHandler handler="TypeHandler.DateTypeHandler"></typeHandler>
</typeHandlers>

测试

@Test
public void testDateHandler() throws IOException {Customer customer = new Customer();customer.setUsername("tom");customer.setPassword("123456");customer.setBalance(25);customer.setBirthday(new Date());InputStream resourceAsStream = Resources.getResourceAsStream("MybatisConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession(true);CustomerDao customerDao = sqlSession.getMapper(CustomerDao.class);customerDao.add(customer);sqlSession.close();
}

查看日志信息,可以看到成功将Date型转换为时间戳

15:10:57,022 DEBUG add:159 - ==> Preparing: insert into customer values(?,?,?,?,?)
15:10:57,054 DEBUG add:159 - > Parameters: 0(Integer), tom(String), 123456(String), 25(Integer), 1651821056429(Long)
15:10:57,062 DEBUG add:159 - < Updates: 1

7.插件机制

MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用,可以使用第三方的插件来对功能进行扩展。

下面演示使用分页助手插件:

分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即 可获得分页的相关数据

开发步骤:

① 导入通用PageHelper的坐标

<!-- 分页助手 -->
<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>

② 在mybatis核心配置文件中配置PageHelper插件

<plugins><!-- 注意:分页助手的插件 配置在environment之前--><plugin interceptor="com.github.pagehelper.PageHelper"><!-- 指定方言,因为不同的数据库的分页实现语句是不同的,如mysql用limit 我只用了mysql环境,所以方言只配了mysql--><property name="dialect" value="mysql"/></plugin>
</plugins>

③ 测试分页数据获取

public void testPageHelper() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("MybatisConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession(true);CustomerDao customerDao = sqlSession.getMapper(CustomerDao.class);//设置查找第2页,每页显示3条数据PageHelper.startPage(2,3);List<Customer> list = customerDao.findAll();//遍历listfor (Customer customer : list) {System.out.println(customer);}//直接打印listSystem.out.println(list);//PageInfo对象,获取分页数据PageInfo<Customer> pageInfo = new PageInfo<>(list);System.out.println("总条数:"+pageInfo.getTotal());System.out.println("总页数:"+pageInfo.getPages());System.out.println("当前页:"+pageInfo.getPageNum());System.out.println("每页显示长度:"+pageInfo.getPageSize());System.out.println("是否第一页:"+pageInfo.isIsFirstPage());System.out.println("是否最后一页:"+pageInfo.isIsLastPage());sqlSession.close();
}

控制台输出及日志信息如下

16:13:26,217 DEBUG findAll_PageHelper:159 - ==> Preparing: select * from customer limit ?,?
16:13:26,218 DEBUG findAll_PageHelper:159 - > Parameters: 3(Integer), 3(Integer)
16:13:26,220 DEBUG findAll_PageHelper:159 - < Total: 2
Customer{id=4, username=‘lisi’, password=‘123456’, balance=25, birthday=Fri May 06 15:09:58 CST 2022}
Customer{id=5, username=‘tom’, password=‘123456’, balance=25, birthday=Fri May 06 15:10:56 CST 2022}
直接打印list
Page{pageNum=2, pageSize=3, startRow=3, endRow=6, total=5, pages=2, reasonable=false, pageSizeZero=false}
总条数:5
总页数:2
当前页:2
每页显示长度:3
是否第一页:false
是否最后一页:true

分页助手主要用到静态方法startPage来设置当前页和每页显示条数。使用该方法后,list的toString方法会被重写,所以直接打印list不会打印返回的Customer数据。要获得数据,可以使用增强for循环。

另外,PageInfo对象可以获得分页详细信息,构造函数需要传入list作为参数。

五.相应API

1.工具对象

我们使用Mybatis时,主要用到了如下几个对象

InputStream inputStream = Resources.getResourceAsStream("MybatisConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

以下表格列出这些对象及其作用

对象 作用
Resources Mybatis提供的一个用于帮助你从类路径下、文件系统或 一个 web URL 中加载资源文件。
SqlSessionFactoryBuilder 用于创建会话工厂
SqlSessionFactory 用于获取数据库会话
SqlSession 使用此对象与数据库进行交互

2.openSession方法

该方法用于从会话工厂中获取会话对象

openSession方法具有重载形式。

方法 说明
openSession() 会默认开启一个事务,但事务不会自动提交,也就意味着需要手动提 交该事务,更新操作数据才会持久化到数据库中
openSession(boolean autoCommit) 参数为是否自动提交,如果设置为true,那么不需要手动提交事务

3.SqlSession会话对象

SqlSession 实例在 MyBatis 中是非常强大的一个类。在这里你会看到所有执行语句、提交或回滚事务和获取映射器实例的方法。

执行语句的方法主要有:

<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)

操作事务的主要方法有:

void commit()
void rollback()

六.Mybatis的Dao层实现方式

1.代理开发方式介绍

采用 Mybatis 的代理开发方式实现 DAO 层的开发,这种方式是企业级开发的主流。 Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis 框架根据接口定义创建接口的动态代理对象,被代理对象的方法体同上边测试方法体类似。

Mapper 接口开发需要遵循以下规范:

1、Mapper.xml文件中的namespace与mapper接口的全包名相同

2、Mapper接口方法名和Mapper.xml中定义的每个statement的id相同

3、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同

4、Mapper接口方法的返回参数类型和mapper.xml中定义的每个sql的resultType的类型相同

测试用例

public void serviceTest() throws IOException {InputStream config = Resources.getResourceAsStream("MybatisConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);SqlSession sqlSession = sqlSessionFactory.openSession();//获得MyBatis框架生成的UserDao接口的实现类UserDao dao = sqlSession.getMapper(UserDao.class);User user = dao.find(5);System.out.println(user);sqlSession.close();
}

使用getMapper()方法获取代理实现类对象,需要接口的class对象作为参数

七.多表操作

1.一对一、一对多查询

  • 一对一:

    • 用户表和订单表的关系为:一个用户有多个订单,一个订单只从属于一个用户
    • 需求:查询一个订单,与此同时查询出该订单所属的用户
  • 一对多:
    • 用户表和订单表的关系为:一个用户有多个订单,一个订单只从属于一个用户
    • 需求:查询一个用户,与此同时查询出该用户具有的订单

查询语句如下:

一对一:定义多查询一个别名为oid的字段,便于区分两张表的同名字段id

select *,orders.id oid from user,orders where user.id = orders.uid;

一对多:等值连接不存在公共属性上值相等的元组(悬浮元组)会被丢弃,为了使没有订单的用户也被查询出来,我们可以用外连接。使用左外连接,这样子左悬浮元组也能被展示出来。用户即使没下订单,也能被查询出来,更符合业务需求

select *,orders.id oid from user left join orders on user.id = orders.uid;

定义Order、User实体如下:

public class Order {private int id;private String product;private int total;//代表该订单从属哪个用户private User user;
}
public class User {private int id;private String name;private String password;//代表该用户所有的订单private List<Order> orders;
}

创建OrderDao接口

public interface OrderDao {public List<Order> findAllOrder();public List<User> findUserWithOrder();
}

映射文件配置如下:

<mapper namespace="Dao.OrderDao"><!--一对一,一个订单——一个用户--><resultMap id="allorder" type="order"><id column="oid" property="id"></id><result column="product" property="product"></result><result column="total" property="total"></result><!--第一中映射自定义对象的方法<result column="uid" property="user.id"></result><result column="name" property="user.name"></result><result column="password" property="user.password"></result>--><!--第二种映射自定义对象的方法--><association property="user" javaType="user"><result column="uid" property="id"></result><result column="name" property="name"></result><result column="password" property="password"></result></association></resultMap><select id="findAllOrder" resultMap="allorder">select *,orders.id oid from user,orders where user.id = orders.uid;</select><!--一对多 一个用户——多个订单--><resultMap id="alluser" type="user"><id column="id" property="id"></id><result column="name" property="name"></result><result column="password" property="password"></result><collection property="orders" ofType="order"><result column="oid" property="id"></result><result column="product" property="product"></result><result column="total" property="total"></result></collection></resultMap><select id="findUserWithOrder" resultMap="alluser">select *,orders.id oid from user left join orders on user.id = orders.uid;</select>
</mapper>

一对一测试

@Test
public void testOneToOne() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("MybatisConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession(true);OrderDao mapper = sqlSession.getMapper(OrderDao.class);List<Order> allOrder = mapper.findAllOrder();for (Order order : allOrder) {System.out.println(order);}sqlSession.close();
}

10:19:09,026 DEBUG findAllOrder:159 - <== Total: 6
Order{id=1, product=‘蚊帐’, total=10, user=User{id=1, name=‘aaa’, password=‘bbb’, orders=null, roles=null}}
Order{id=2, product=‘文章’, total=20, user=User{id=1, name=‘aaa’, password=‘bbb’, orders=null, roles=null}}
Order{id=3, product=‘水杯’, total=5, user=User{id=2, name=‘wangwu’, password=‘987654’, orders=null, roles=null}}
Order{id=5, product=‘铅笔’, total=6, user=User{id=6, name=‘zhaoliu’, password=‘68899’, orders=null, roles=null}}
Order{id=6, product=‘筷子’, total=10, user=User{id=1, name=‘aaa’, password=‘bbb’, orders=null, roles=null}}
Order{id=7, product=‘抹布’, total=2, user=User{id=2, name=‘wangwu’, password=‘987654’, orders=null, roles=null}}

一对多测试

@Test
public void testOneToMany() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("MybatisConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession(true);OrderDao mapper = sqlSession.getMapper(OrderDao.class);List<User> allUser = mapper.findUserWithOrder();for (User user : allUser) {System.out.println(user);}sqlSession.close();
}

User{id=1, name=‘aaa’, password=‘bbb’, orders=[Order{id=1, product=‘蚊帐’, total=10, user=null}, Order{id=2, product=‘文章’, total=20, user=null}, Order{id=6, product=‘筷子’, total=10, user=null}], roles=null}
User{id=2, name=‘wangwu’, password=‘987654’, orders=[Order{id=3, product=‘水杯’, total=5, user=null}, Order{id=7, product=‘抹布’, total=2, user=null}], roles=null}
User{id=6, name=‘zhaoliu’, password=‘68899’, orders=[Order{id=5, product=‘铅笔’, total=6, user=null}], roles=null}
User{id=4, name=‘wangwu1’, password=‘123456’, orders=[], roles=null}
User{id=5, name=‘tianqi’, password=‘68989’, orders=[], roles=null}

2.多对多查询

  • 用户表和角色表的关系为:一个用户有多个角色,一个角色被多个用户使用
  • 需求:查询用户同时查询出该用户的所有角色

查询语句如下

select * from user_table,urrel,role_table where user_table.id = userid and role_table.id = roleid;

定义Role如下:

public class Role {private int id;private String roleName;private String roleDesc;
}

为User添加Role集合属性

private List<Role> roles;

在UserDao接口中添加方法

public List<User> findUserAndRole();

映射文件配置如下

<!--多对多 多个用户——多个角色-->
<resultMap id="userandrole" type="user"><id column="id" property="id"></id><result column="username" property="name"></result><result column="password" property="password"></result><collection property="roles" ofType="role"><result column="id" property="id"></result><result column="roleName" property="roleName"></result><result column="roleDesc" property="roleDesc"></result></collection>
</resultMap>
<select id="findUserAndRole" resultMap="userandrole">select * from user_table,urrel,role_table where user_table.id = userid and role_table.id = roleid;
</select>

多对多测试

@Test
public void testManyToMany() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("MybatisConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream,"forrole");SqlSession sqlSession = sqlSessionFactory.openSession(true);UserDao mapper = sqlSession.getMapper(UserDao.class);List<User> userAndRole = mapper.findUserAndRole();for (User user : userAndRole) {System.out.println(user.getName());for (Role role : user.getRoles()) {System.out.println(role);}System.out.println("-----------------------------");}sqlSession.close();
}

王明
Role{id=1, roleName=‘级长’, roleDesc=‘管理年级’}
-----------------------------
lisi
Role{id=5, roleName=‘校长’, roleDesc=‘管理学校’}
Role{id=5, roleName=‘级长’, roleDesc=‘管理年级’}

配置详解:

标签 作用
resultMap 一般用于多表查询,如返回结果涉及封装多个对象时
id 用于主键字段
association 返回数据映射的属性包含一个自定义实体对象时使用
collection 返回数据映射的属性包含一个自定义实体对象集合时使用
属性 作用 应用标签
property 代表实体对象属性名 result、assocation、collection
column 代表数据库字段名 result
javaType 代表实体类1 assoaction
ofType 代表集合实体类 collection

对于查询出来的结果对应的实体属性对象里存在包含自定义类型对象属性的情况,可以使用association标签,或用property属性,但是要以实体.属性名的形式

3.总结

一对一配置:使用<resultMap>+<association>做配置
一对多配置:使用<resultMap>+<collection>做配置
多对多配置:使用<resultMap>+<collection>做配置

八.Mybatis注解开发

xml配置不免繁琐,注解开发已然成为一种趋势。与Spring框架一样,我们可以使用xml配置文件的形式,也可以使用注解的形式进行开发。使用注解形式进行开发,就可以减少Mapper文件的编写了。

1.注解扫描

修改MyBatis的核心配置文件,我们使用了注解替代的映射文件,所以我们只需要加载使用了注解的Mapper接口即可

<mappers>
<!--扫描使用注解的类或包--><mapper class="POJO.UserDao"></mapper><mapper class="POJO"></mapper>
</mappers>

2.注解详解

注解写在Dao层方法上即可,Mybatis会通过反射机制,帮助我们创建出对应的实现类

注解 作用
@Insert
@Delete
@Update
@Select
@Results 相当于resultMap标签,该注解中可以使用单个@Result注解,也可以使用@Result集合。使用格式:@Results({@Result(),@Result()})或@Results(@Result())
@Result 相当于result和id标签,配置于Results注解中。result注解id属性为true 表明该字段是主键,替代id标签
@One 相当于association标签,实现一对一结果封装。是多表查询的关键,在注解中用来指定子查询返回单一对象。
@Many 相当于collection标签,实现一对多,多对多结果集封装。是是多表查询的关键,在注解中用来指定子查询返回对象集合。

  1. 如果在核心配置文件中为某个实体类定义了别名,则可以直接填别名,例如这里对User类定义了别名user,所以值直接写了user ↩︎

【Java必学框架】一文搞懂Java持久层框架Mybatis,由浅入深相关推荐

  1. java吵醒线程_一文搞懂 Java 线程中断

    在之前的一文<如何"优雅"地终止一个线程>中详细说明了 stop 终止线程的坏处及如何优雅地终止线程,那么还有别的可以终止线程的方法吗?答案是肯定的,它就是我们今天要分 ...

  2. java sleep方法_一文搞懂 Java 线程中断!

    在之前的一文<如何"优雅"地终止一个线程>详细说明了 stop 终止线程的坏处及如何优雅地终止线程,那么还有别的可以终止线程的方法吗?答案是肯定的,它就是我们今天要分享 ...

  3. java构造器 权限_一文搞懂Java的 构造方法 和 访问权限

    目录 零.前言 Java是一门当今最火的编程语言之一,拥有很多现成可用的库,在我们编程生涯中,有着无比重要的地位. Java中有个概念叫做访问权限.它们是什么呢?今天我来详细讲解. 本文所有代码已经上 ...

  4. 一文搞懂 Java 线程中断

    转载自   一文搞懂 Java 线程中断 在之前的一文<如何"优雅"地终止一个线程>中详细说明了 stop 终止线程的坏处及如何优雅地终止线程,那么还有别的可以终止线程 ...

  5. java rest 序列化_一文看懂Java序列化

    一文看懂Java序列化 简介 首先我们看一下wiki上面对于序列化的解释. 序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓 ...

  6. 夯实Java基础系列19:一文搞懂Java集合类框架,以及常见面试题

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  7. 夯实Java基础系列17:一文搞懂Java多线程使用方式、实现原理以及常见面试题

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  8. 一文搞懂Java日志级别,重复记录、丢日志问题

    1 SLF4J 日志行业的现状 框架繁 不同类库可能使用不同日志框架,兼容难,无法接入统一日志,让运维很头疼! 配置复杂 由于配置文件一般是 xml 文件,内容繁杂!很多人喜欢从其他项目或网上闭眼co ...

  9. 一文搞懂 Java 泛型,非常详细!

    作者: ZiWenXie http://www.ziwenxie.site/2017/03/01/java-generic/ 引言 泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广 ...

  10. java arraylist排序_一文读懂Java集合框架

    欢迎关注微信公众号:深入浅出Java源码 概念 Java集合框架为程序员提供了预先包装的数据结构和算法来操纵他们.集合框架被设计成要满足以下几个目标. 该框架必须是高性能的.基本集合(动态数组,链表, ...

最新文章

  1. 学会使用函数式编程的程序员(第2部分)
  2. 【数据挖掘】卷积神经网络 ( 池化 | 丢弃 | 批量规范化 | 卷积神经网络完整流程示例 | 卷积 | 池化 | 全连接 | 输出 | 卷积神经网络总结 )
  3. vue学习:v-on
  4. word无法启动转换器recovr32_迅捷PDF转换器3.0.1Mod会员版
  5. 文件夹错误 分配句柄_重启数据库遇到错误ORA27154,ORA27300,ORA27301,ORA27302
  6. Transfomer入门:Self-attention + Multi-head Self-attention
  7. DataGridView控件 1129
  8. OpenGL和D3D11中的深度模版测试
  9. 令人厌恶的错误MSB3721,以及win10,VS2019,YOLO V4 环境搭建
  10. 计算机主机异常经常蓝屏,电脑频繁蓝屏怎么办
  11. 如何删除双系统中的其中一个(完全删除)
  12. 烧光20亿不够续命,快狗打车IPO找钱?
  13. 抓阄 计算机代表什么东西,周岁抓阄准备哪些东西
  14. 用python做课表_python模拟登陆urp教务处选课抓取课表
  15. 数据结构与算法——赫夫曼树基本实现
  16. Java实验——设计一个数组模型,用于存储体育项目成绩男生体育项目有足球、长跑和铅球,女生体育项目有跳舞、体操、游泳。设计排序算法,将变量a、b、c中的数值按大小顺利进行互换(从大到小排列)。
  17. 初探 MacBook Pro 刘海屏
  18. Ctrl+shift+k会调出搜狗输入法软键盘,与VSCode行删除快捷键冲突
  19. 5V降压1.8V芯片,稳压电路设计建议PW2059
  20. 使用Excel 表示汽车、摩托车10年免检时间、非常清晰。

热门文章

  1. c语言写一个五子棋小游戏
  2. LitJson扩展基础类型
  3. 易语言网络验证UI界面源码
  4. 一天入门51单片机教程
  5. linux-uboot 移植四 uboot的移植
  6. java分页工具集合
  7. Ubuntu下安装VSCODE并输入中文
  8. ECharts 实现地图功能
  9. (时间表达式)定时任务Quartz 之 cron表达式及在线生成器
  10. 语言模型(NNLM)