前言
:CSDN的小伙伴们大家好,今天跟大家分享一个我们后台开发使用的框架——Mybatis。如果这篇文章对你有用,麻烦给我点个小赞以示鼓励吧
:博客主页:空山新雨后的java知识图书馆
☀️:天气晴朗。
:成大功者,不顾小嫌;建远略者,不期近效。——陈宏谋
上一篇文章:旅游网站后台信息管理——简单版(增删改查)
欢迎大家一起学习,进步。加油
mybatis的百度百科定义:
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。
那么通过学习mybatis可以更简单的完成传统开发中Dao层的操作,大大提高了开发者的开发效率,使我们的注意力主要集中在SQL语句上面即可。
要想使用mybatis,首先你得下载mybatis的jar包。下载地址:mybatis——http://www.mybatis.org/mybatis-3/
maven项目中引用mybatis的坐标即可。
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.32version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2 version>
<scope>testscope>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.12version>
dependency>
dependencies>
package com.study.domain;
/**
* @author wang
* @version 1.0
* @packageName com.study.domain
* @className User
* @date 2022/4/18 10:11
* @Description User实体类
*/
public class User {
private int id;
private String username;
private String password;
注意提供set,get方法,篇幅限制,这里就不演示set,get方法了。
文件名字随你的喜好取就行。
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="int">
delete from user_u where id=#{id}
delete>
<update id="update" parameterType="com.study.domain.User">
update user_u set username=#{username},password=#{password} where id=#{id};
update>
<insert id="saveUser" parameterType="com.study.domain.User">
insert into user_u(username,password) values(#{username},#{password});
insert>
<select id="findAll" resultType="user">
select * from user_u
select>
<select id="findOne" resultType="user" parameterType="int">
select * from user_u where id=#{id}
select>
mapper>
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="com.study.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="com\study\mapper\UserMapper.xml">mapper>
mappers>
configuration>
jdbc.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root
这里的测试只演示一个,我演示的查询。
/**
* @Date 2022/4/18 10:50
* @Param
* @Return void
* @MetodName test1
* @Author wang
* @Description 该方法测试第一个SQL语句,用mybatis框架实现的
*/
@Test
public void test1() throws IOException {
//获得核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapManager.xml");
//获取SQLsession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获得session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行操作,参数是配置文件中namespace.id的去获得唯一的SQL语句
List<User> users = sqlSession.selectList("userMapper.findAll");
System.out.println(users);
//释放资源
sqlSession.close();
}
这里的UserMapper.findAll指向的就是配置文件中的id为findAll的那条SQL语句。
另外这里只演示测试,如果要是增删改,必须手动提交事务,才能生效。
有两种做法提交事务,
第一种: SqlSession sqlSession = sqlSessionFactory.openSession(true);
将openSession传入一个参数,true,就可以自动提交事务了。
第二种,就是自己手动提交了。sqlSession.commit();
测试结果:
User{id=1, username='张三', password='123456'}
User{id=2, username='lisi', password='123465'}
User{id=4, username='周瑜', password='45454545'}
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
映射文件的DTD约束头
<mapper namespace="userMapper">
根标签,其中namespace为命名空间,与子标签中的id一起组成查询的标识
<select id="findAll" resultType="com.study.domain.User">
子标签,执行的操作,insert,update等,resultType为查询结果对应的实体类型
Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的,有些时候业务逻辑复杂时,我们的 SQL是动态变化的,此时在前面的学习中我们的 SQL 就不能满足要求了.
我们根据实体类的不同取值,使用不同的 SQL语句来进行查询。比如在 id如果不为空时可以根据id查询,如果username 不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。
配置文件代码
<mapper namespace="com.study.Dao.UserDao">
<select id="findByCondition" parameterType="user" resultType="user">
select * from user_u
<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>
上述代码会自动根据条件,将后续的SQL进行拼接。
测试代码部分
//获取代理实现
UserDao mapper = sqlSession.getMapper(UserDao.class);
//模拟条件user
User user = new User();
user.setId(1);
// user.setUsername("张三");
// user.setPassword("123456");
//执行方法
// User byCondition = mapper.findByCondition(user);
// System.out.println(byCondition);//User{id=1, username='张三', password='123456'}
循环执行sql的拼接操作,例如:SELECT * FROM USER WHERE id IN (1,2,5)。
配置文件代码
<select id="findByIds" parameterType="list" resultType="user" >
/* 这里原始的查询语句,无法达到动态的使用效果,因此使用foreach进行改进
select * from user_u where id in(1,2,3);*/
select * from user_u
<where>
/* 语句中Collection对应的是传进来的参数,我们这里传进来的是一个list集合,因此,写list,
open对应的是语句以什么开始拼接,close对应的是语句以什么结束拼接,separator是分隔符,也就是参数之间的分隔符,为,
item就是每一个参数变量,我们用的item*/
<foreach collection="list" open="id in(" close=")" separator="," item="id">
/*这里就只需要对参数进行接收就可以对集合中的每个参数进行拼接。*/
#{id}
foreach>
where>
select>
测试代码部分
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
List<User> userList = mapper.findByIds(ids);
System.out.println(userList);
//[User{id=1, username='张三', password='123456'}, User{id=2, username='lisi', password='123465'}]
foreach标签的属性含义
标签用于遍历集合,它的属性:
•collection:代表要遍历的集合元素,注意编写时不要写#{}
•open:代表语句的开始部分
•close:代表结束部分
•item:代表遍历集合的每个元素,生成的变量名
•sperator:代表分隔符
SQL中可将重复的SQL提取出来,使用时用include引用即可,最终达到SQL重用的目的
代码演示
<sql id="selectUser">select * from user_usql>
<select id="findByCondition" parameterType="user" resultType="user">
/*引用抽取的SQL语句*/
<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>
语法上需要用到<SQL>标签和<include>标签
<sql>标签进行抽取
<include>标签进行引用
configuration 配置
properties 属性
settings 设置
typeAliases 类型别名
typeHandlers 类型处理器
objectFactory 对象工厂
plugins 插件
environments 环境
environment 环境变量
transactionManager 事务管理器
DataSource 数据源
dataBaseldProvider 数据库厂商标识
mappers 映射器
其中,事务管理器(transactionManager)类型有两种:
•JDBC:这个配置就是直接使用了JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
•MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false来阻止它默认的关闭行为。
其中,数据源(dataSource)类型有三种:
•UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
•POOLED:这种数据源的实现利用“池”的概念将 JDBC连接对象组织起来。
•JNDI:这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
该标签的作用是加载映射的,加载方式有如下几种
•使用相对于类路径的资源引用:
•使用完全限定资源定位符(URL),例如:
•使用映射器接口实现类的完全限定类名,例如:
•将包内的映射器接口实现全部注册为映射器,例如:
实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件
类型别名是为Java 类型设置一个短的名字。原来的类型名称配置如下
配置typeAliases,为com.itheima.domain.User定义别名为user
注意:
配置的typeAliases标签必须在properties的标签后面,否则会报错
上面我们是自定义的别名,mybatis框架已经为我们设置好的一些常用的类型的别名
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器(截取部分)。
你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。具体做法为:
实现 org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的org.apache.ibatis.type.BaseTypeHandler,
然后可以选择性地将它映射到一个JDBC类型。
例如需求:一个Java中的Date数据类型,我想将之存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换。
开发步骤
①定义转换类继承类BaseTypeHandler
②覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时 mysql的字符串类型转换成 java的Type类型的方法
③在MyBatis核心配置文件中进行注册
④测试转换是否正确
handler代码演示
package com.study.handler;
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;
/**
* @author wang
* @version 1.0
* @packageName com.study.handler
* @className DateTypeHandler
* @date 2022/4/19 16:15
* @Description 自定义类型转换处理器
*/
public class DateTypeHandler extends BaseTypeHandler<Date> {
@Override
/**
* 将date类型转换为毫秒值
*/
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
long time = date.getTime();
preparedStatement.setLong(i,time);
}
/**
* 将毫秒值转换为date
* @param resultSet
* @param s
* @return
* @throws SQLException
*/
@Override
public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
long aLong = resultSet.getLong(s);
Date date = new Date(aLong);
return date;
}
@Override
public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
long aLong = resultSet.getLong(i);
Date date = new Date(aLong);
return date;
}
@Override
public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
long aLong = callableStatement.getLong(i);
Date date = new Date(aLong);
return date;
}
}
核心配置文件演示
<typeHandlers>
<typeHandler handler="com.study.handler.DateTypeHandler">typeHandler>
typeHandlers>
MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据
开发步骤
①导入通用PageHelper的坐标
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>3.7.5version>
dependency>
<dependency>
<groupId>com.github.jsqlparsergroupId>
<artifactId>jsqlparserartifactId>
<version>0.9.1version>
dependency>
②在mybatis核心配置文件中配置PageHelper插件
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql"/>
plugin>
③测试分页数据获取
PageHelper.startPage(2, 3);
//User{id=5, username='周瑜', password='45454545', date=Tue Apr 19 17:07:50 CST 2022}
//User{id=6, username='周瑜', password='45454545', date=Tue Apr 19 17:08:00 CST 2022}
//User{id=7, username='周瑜', password='45454545', date=Tue Apr 19 17:08:04 CST 2022}
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.getPrePage());
System.out.println("下一页:" + pageInfo.getNextPage());
System.out.println("是否是第一页:" + pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:" + pageInfo.isIsLastPage());
System.out.println("当前显示条目数:" + pageInfo.getPageSize());
System.out.println("总条目数;" + pageInfo.getTotal());
System.out.println("总页数:" + pageInfo.getPages());
System.out.println("数据库查询的开始条目:第" + pageInfo.getStartRow() + "条");
/*
* 当前页码:2
上一页:1
下一页:3
是否是第一页:false
是否是最后一页:false
当前显示条目数:3
总条目数;10
总页数:4
数据库查询的开始条目:第4条*/
通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象
代码演示
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 中加载资源文件。
导包:import org.apache.ibatis.io.Resources;
SqlSessionFactory 有多个个方法创建SqlSession 实例。常用的有如下两个:
方法 | 解释 |
---|---|
openSession() | 会默认开启一个事务,但事务不会自动提交,也就意味着需要手动提交该事务,更新操作数据才会持久化到数据库中 |
openSession(boolean autoCommit) | 参数为是否自动提交,如果设置为true,那么不需要手动提交事务 |
SqlSession 实例在 MyBatis 中是非常强大的一个类。在这里你会看到所有执行语句、提交或回滚事务和获取映射器实例的方法。
执行语句的方法主要有
:查找一个对象
T selectOne(String statement, Object parameter)
:查找一个集合的对象
List 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()
:回滚
<insert id="saveUser" parameterType="com.study.domain.User">
insert into user_u(username,password) values(#{username},#{password});
insert>
/**
* 测试插入语句
*/
@Test
public void test2() throws IOException {
//模拟插入对象的值
User user = new User();
user.setUsername("park");
user.setPassword("123");
//加载核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapManager.xml");
//获取SQLsession工厂的对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获取session会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.insert("userMapper.saveUser", user);
//提交事务,如果增删改操作不提交事务,是无法对数据库表进行修改操作的。
sqlSession.commit();
//释放资源
sqlSession.close();
}
•插入语句使用insert标签
•在映射文件中使用parameterType属性指定要插入的数据类型(指定全包名)
•Sql语句中使用#{实体属性名}方式引用实体中的属性值
•插入操作使用的API是sqlSession.insert(“命名空间.id”,实体对象);
•插入操作涉及数据库数据变化,所以要使用sqlSession对象显示的提交事务,即sqlSession.commit()
代码演示
<update id="update" parameterType="com.study.domain.User">
update user_u set username=#{username},password=#{password} where id=#{id};
update>
/**
* 测试修改操作
*/
@Test
public void update() throws IOException {
//模拟修改的对象
User user = new User();
user.setId(3);
user.setUsername("lucy");
user.setPassword("555");
//获取核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapManager.xml");
//获取SQLsession的工厂对象
SqlSessionFactory sqlsessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获取session会话对象
SqlSession sqlSession = sqlsessionFactory.openSession();
//执行SQL
sqlSession.update("userMapper.update",user);
//提交事务
sqlSession.commit();
//关闭资源
sqlSession.close();
}
•修改语句使用update标签
•修改操作时使用的API是sqlSession.update(“命名空间.id”,实体对象);
<delete id="delete" parameterType="java.lang.Integer">
delete from user_u where id=#{id}
delete>
/**
* 测试删除操作
*/
@Test
public void delete() throws IOException {
//获取核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapManager.xml");
//获取SQLsession工厂对象
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获取session会话对象
SqlSession sqlSession = build.openSession();
//执行sql
sqlSession.delete("userMapper.delete",3);
//提交事务
sqlSession.commit();
//关闭资源
sqlSession.close();
}
• 删除语句使用delete标签
•Sql语句中使用#{任意字符串}方式引用传递的单个参数
•删除操作使用的API是sqlSession.delete(“命名空间.id”,Object);
以前我们写dao层的实现,就是dao层写一个实现类,并实现其中的方法,达到目的。
今天我们使用mybatis提供的代理开发方式实现 DAO 层的开发,这种方式是我们后面进入企业的主流。
Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis 框架根据接口定义创建接口的动态代理对象,代理对象的方法体同Dao接口实现类方法。
1) Mapper.xml文件中的namespace与mapper接口的全限定名相同
2) Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3) Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
4) Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
mapper接口:
public interface UserMapper {
public List<User> findAll();
public User findById(int id);
}
mapper配置文件代码演示
<mapper namespace="com.study.Dao.UserMapper">
<select id="findAll" resultType="user">
select * from user_u
select>
<select id="findById" resultType="user" parameterType="int">
select * from user_u where id=#{id}
select>
mapper>
测试代码:
public static void main(String[] args) throws IOException {
//获取核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapManager.xml");
//获取SQLsessionfactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获取SQLsession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取代理实现
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//执行方法
List<User> all = mapper.findAll();
System.out.println(all);//[User{id=1, username='张三', password='123456'}, User{id=2, username='lisi', password='123465'}]
User user = mapper.findById(1);
System.out.println(user);//User{id=1, username='张三', password='123456'}
}
注意:这里的resultType写的user,是因为我在核心配置文件中给这个实体类取的别名叫user。
<typeAliases>
<typeAlias type="com.study.domain.User" alias="user">typeAlias>
typeAliases>
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户
order表
private int id;
private Date orderTime;
private double total;
//该订单的所属用户
private User user;
数据库对应着创建即可
重点就是userMapper中的封装
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.study.mapper.OrderMapper">
<resultMap id="selectAll" 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="selectAll">
SELECT *,o.`id` oid FROM user_u u,`order` o WHERE o.`uid`=u.`id`;
select>
mapper>
测试代码:
@Test
/**
* 测试查蕈order订单信息
*/
public void testSelect() throws IOException {
//加载核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获取SQLSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获取SQLSession对象
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();
/*Order{id=6, orderTime=Thu Jan 01 08:00:02 CST 1970, total=0.0, user=User{id=1, username='张三', password='123456', birthday=null, orderList=null}}
Order{id=7, orderTime=Thu Jan 01 08:00:02 CST 1970, total=4545.0, user=User{id=2, username='lisi', password='123465', birthday=null, orderList=null}}
Order{id=8, orderTime=Thu Jan 01 08:00:02 CST 1970, total=4222.0, user=User{id=1, username='张三', password='123456', birthday=null, orderList=null}}
Order{id=9, orderTime=Thu Jan 01 08:00:02 CST 1970, total=1111.0, user=User{id=1, username='张三', password='123456', birthday=null, orderList=null}}
Order{id=10, orderTime=Thu Jan 01 08:00:02 CST 1970, total=4545.0, user=User{id=1, username='张三', password='123456', birthday=null, orderList=null}}*/
}
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单
User实体
public class User {
private int id;
private String username;
private String password;
private Date birthday;
//用于存储对应的order
private List<Order> orderList;
UserMapper接口:
public interface UserMapper {
public List<User> findAll();
}
UserMapper的配置
<mapper namespace="com.study.mapper.UserMapper">
<resultMap id="selectAll" 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="selectAll">
select * ,o.id oid from user_u u,`order` o where o.uid=u.id
select>
mapper>
测试结果
/**
*测试一对多的关系
*/
@Test
public void testSe() throws IOException {
//加载核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获取SQLSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获取SQLSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取代理实现
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> all = mapper.findAll();
for (User user : all) {
System.out.println(user);
}
sqlSession.close();
/*User{id=1, username='张三', password='123456', birthday=null, orderList=[Order{id=6, orderTime=Thu Jan 01 08:00:02 CST 1970, total=0.0, user=null}, Order{id=8, orderTime=Thu Jan 01 08:00:02 CST 1970, total=4222.0, user=null}, Order{id=9, orderTime=Thu Jan 01 08:00:02 CST 1970, total=1111.0, user=null}, Order{id=10, orderTime=Thu Jan 01 08:00:02 CST 1970, total=4545.0, user=null}]}
User{id=2, username='lisi', password='123465', birthday=null, orderList=[Order{id=7, orderTime=Thu Jan 01 08:00:02 CST 1970, total=4545.0, user=null}]}*/
}
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
多对多查询的需求:查询用户同时查询出该用户的所有角色
Role实体
public class Role {
private int id;
private String roleName;
private String roleDesc;
注意提供set,get方法
UserMapper
public List<User> findRole();
配置UserMapper.xml
<resultMap id="RoleMap" type="user">
<id column="userId" 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="roleId" property="id">id>
<result column="roleName" property="roleName">result>
<result column="roleDesc" property="roleDesc">result>
collection>
resultMap>
<select id="findRole" resultMap="RoleMap">
select * from user_u u ,user_role ur,role r where u.id=ur.userId and r.id = ur.roleId
select>
测试结果:
/**
* 测试多对多
*/
@Test
public void testRole() throws IOException {
//加载核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获取SQLSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获取SQLSession的对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取代理实现
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> all = mapper.findRole();
for (User user : all) {
System.out.println(user);
}
sqlSession.close();
/*User{id=1, username='张三', password='123456', birthday=null, orderList=null, roleList=[Role{id=1, roleName='院长', roleDesc='管理学院'}, Role{id=3, roleName='教授 ', roleDesc='讲授知识'}]}
User{id=2, username='lisi', password='123465', birthday=null, orderList=null, roleList=[Role{id=2, roleName='副院长', roleDesc='辅助管理学院'}]}
User{id=4, username='周瑜', password='45454545', birthday=Tue Apr 19 16:21:43 CST 2022, orderList=null, roleList=[Role{id=3, roleName='教授 ', roleDesc='讲授知识'}]}*/
}
mybatis多表配置方式
一对一配置:使用做配置
一对多配置:使用+做配置
多对多配置:使用+做配置
这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper映射文件了。
mybatis常用注解
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
之前我们是通过加载映射文件来进行开发,那么使用注解我们就不需要加载映射文件了,那么有两种方式可以用来开发
两种方式修改加载核心配置文件
<mappers>
<mapper class="com.itheima.mapper.UserMapper">mapper>
mappers>
<mappers>
<package name="com.itheima.mapper">package>
mappers>
实现复杂关系映射之前我们可以在映射文件中通过配置来实现,使用注解开发后,我们可以使用@Results注解,@Result注解,@One注解,@Many注解组合完成复杂关系的配置
注解 | 说明 |
---|---|
@Results | 代替的标签是 该注解中可以使用单个@Result注解,也可以使用@Result集合,使用格式:@Result({@Result(),@Result()})或@Results(@Result()) |
@Result | 代替了 @Result中属性介绍:column:数据库的列名;property:需要装配的属性名;one:需要使用@one注解(@Result(one=@one)());many:需要使用many注解(@Result(mang=@many)()) |
@one(一对一) | 代替了 标签,是多表查询的关键,在注解中用来指定子查询返回单一对象;@one注解属性介绍:select:指定用来多表查询的sqlMapper;使用格式:@Result(column=“”,property=“”,one=@One(select=“”) |
@many(多对一) | 代替了标签,是多表查询的关键,在注解中使用指定子查询返回对象的集合。使用格式:@Result(property=“”,column=“”,mang=@many(select=“”)) |
模型:
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户
User和Order的实体类
public class Order {
private int id;
private Date ordertime;
private double total;
//代表当前订单从属于哪一个客户
private User user;
}
public class User {
private int id;
private String username;
private String password;
private Date birthday;
}
OrderMapper接口
public interface OrderMapper {
List<Order> findAll();
}
使用注解配置:
方式一:
@Select("select *,o.id oid from `order` o,user_u u where u.id=o.uid")
@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")
})
public List<Order> findAll();
方式二:
@Select("select *from `order`")
@Results({
@Result(column = "oid", property = "id"),
@Result(column = "ordertime", property = "orderTime"),
@Result(column = "total", property = "total"),
@Result(
//在查询出来的结果中要封装的order中的属性名称,
property = "user",
column = "uid",//另外一个查询需要的参数,根据表中UID的字段,就是user中的id
javaType = User.class,//要封装的实体类型
one = @One(select = "com.study.mapper.UserMapper.findUserById")//一对一的进行封装,调用userMapper中的findUserById的方法,查询处
//查询出被封装好的user对象到该order对象的user属性中
)
})
public List<Order> findAll();
测试结果:
private OrderMapper mapper;
@Before
public void before() throws IOException {
//加载核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获取SQLSessionFactory对象
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获取SQLSession对象
SqlSession sqlSession = build.openSession();
mapper = sqlSession.getMapper(OrderMapper.class);
}
@Test
public void findAll() {
List<Order> all = mapper.findAll();
for (Order order : all) {
System.out.println(order);
}
/*Order{id=6, orderTime=Wed Apr 20 00:00:00 CST 2022, total=0.0, user=User{id=1, username='张三', password='123456', date=null}}
Order{id=7, orderTime=Wed Apr 20 00:00:00 CST 2022, total=4545.0, user=User{id=2, username='lisi', password='123465', date=null}}
Order{id=8, orderTime=Thu Apr 14 00:00:00 CST 2022, total=4222.0, user=User{id=1, username='张三', password='123456', date=null}}
Order{id=9, orderTime=Thu May 26 00:00:00 CST 2022, total=1111.0, user=User{id=1, username='张三', password='123456', date=null}}
Order{id=10, orderTime=Thu Apr 07 00:00:00 CST 2022, total=4545.0, user=User{id=1, username='张三', password='123456', date=null}}*/
}
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单
UserMapper中的注解配置:
@Select("select * from user_u")
@Results({
// id=true给该列添加主键标识
@Result(id=true,column = "id",property = "id"),
@Result(column = "username",property = "username"),
@Result(column = "password",property = "password"),
@Result(
property = "orders",//属性名
column = "id",//被取出进行查询的参数,就user中的id,用于查询order
javaType = List.class,//类型为集合
many = @Many(select = "com.study.mapper.OrderMapper.findByUid")//多对多的关系
)
})
public List<User> findUserAndOrderAll();
OrderMapper中的注解配置:
@Select("select * from `order` where uid = #{uid}")
public Order findByUid(int uid);
测试结果:
@Test
public void test() {
List<User> userAndOrderAll = mapper.findUserAndOrderAll();
for (User user : userAndOrderAll) {
System.out.println(user);
}
/*User{id=1, username='张三', password='123456', birthday=null, orders=[Order{id=6, orderTime=Wed Apr 20 00:00:00 CST 2022, total=0.0, user=null}, Order{id=8, orderTime=Thu Apr 14 00:00:00 CST 2022, total=4222.0, user=null}, Order{id=9, orderTime=Thu May 26 00:00:00 CST 2022, total=1111.0, user=null}, Order{id=10, orderTime=Thu Apr 07 00:00:00 CST 2022, total=4545.0, user=null}]}
User{id=2, username='lisi', password='123465', birthday=null, orders=[Order{id=7, orderTime=Wed Apr 20 00:00:00 CST 2022, total=4545.0, user=null}]}
User{id=4, username='周瑜', password='45454545', birthday=null, orders=[]}
User{id=5, username='周瑜', password='45454545', birthday=null, orders=[]}
User{id=6, username='周瑜', password='45454545', birthday=null, orders=[]}
User{id=7, username='周瑜', password='45454545', birthday=null, orders=[]}
User{id=8, username='周瑜', password='45454545', birthday=null, orders=[]}
User{id=9, username='周瑜', password='45454545', birthday=null, orders=[]}
User{id=10, username='周瑜', password='45454545', birthday=null, orders=[]}
User{id=11, username='周瑜', password='45454545', birthday=null, orders=[]}*/
}
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
多对多查询的需求:查询用户同时查询出该用户的所有角色
注解配置
public interface UserMapper {
@Select("select * from user")
@Results({
@Result(id = true,property = "id",column = "id"),
@Result(property = "username",column = "username"),
@Result(property = "password",column = "password"),
@Result(property = "birthday",column = "birthday"),
@Result(property = "roleList",column = "id",
javaType = List.class,
many = @Many(select = "com.itheima.mapper.RoleMapper.findByUid"))
})
List<User> findAllUserAndRole();}
public interface RoleMapper {
@Select("select * from role r,user_role ur where r.id=ur.role_id and ur.user_id=#{uid}")
List<Role> findByUid(int uid);
}
测试结果:
private UserMapper mapper;
@Before
public void before() throws IOException {
//加载核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//获取SQLSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//获取SQLSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取代理实例
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testRoleAndUser() {
List<User> userAndRoleAll = mapper.findUserAndRoleAll();
for (User user : userAndRoleAll) {
System.out.println(user);
}
/*User{id=1, username='张三', password='123456', birthday=null, orders=null, roleList=[Role{id=1, roleName='院长', roleDesc='管理学院'}, Role{id=3, roleName='教授 ', roleDesc='讲授知识'}]}
User{id=2, username='lisi', password='123465', birthday=null, orders=null, roleList=[Role{id=2, roleName='副院长', roleDesc='辅助管理学院'}]}
User{id=4, username='周瑜', password='45454545', birthday=null, orders=null, roleList=[Role{id=3, roleName='教授 ', roleDesc='讲授知识'}]}*/
}