<1>为什么使用mybatis框架
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
使用mybatis框架来替代原始JDBC的开发。mybatis把连接资源放在内部,想要用直接拿,用完还回来即可,有效的解决了JDBC频繁的创建、释放连接资源。而且我们使用mybatis框架,当sql语句需要发生改变的,不需要改变JAVA代码,只需要更改一下配置文件即可。
<2>mybatis执行的流程
<3>mybatis入门
<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.5.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
package com.liu.domain;
public class User {
private int id;
private String username;
private String email;
private String password;
private String phoneNum;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", email='" + email + '\'' +
", password='" + password + '\'' +
", phoneNum='" + phoneNum + '\'' +
'}';
}
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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
}
mappers 元素则包含了一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息。
<?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="com.liu.domain.User">
select *from user
</select>
</mapper>
XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。后面会再探讨 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="development">
<!--配置一个id为development的环境-->
<environment id="development">
<!--使用JDBC事务-->
<transactionManager type="JDBC"/>
<!--数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///stu?useSSL=false&useUnicode=true&characterEconding=UTF-8&allowPublicKeyRetrieval=true"/>
<property name="username" value="root"/>
<property name="password" value="131411"/>
</dataSource>
</environment>
</environments>
<!--加载映射文件-->
<mappers>
<mapper resource="com.liu.mapper/UserMapper.xml"/>
</mappers>
</configuration>
当然,还有很多可以在 XML 文件中配置的选项,上面的示例仅罗列了最关键的部分。 注意 XML 头部的声明,它用来验证 XML 文档的正确性。environment 元素体中包含了事务管理和连接池的配置。
进行测试是否成功
@Test
public void test01() throws IOException {
//读取mybatis的配置文件得到配置文件流
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//根据配置文件信息,创建会话工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
//通过工厂得到SqlSession对象
SqlSession sqlSession = factory.openSession();
//通过sqlSession对象执行映射文件中定义的SQL,并返回映射结果
List<User> list = sqlSession.selectList("userMapper.findAll");
System.out.println(list);
//关闭sqlSession,释放资源
sqlSession.close();
}
运行结果展示:
<4>mybatis增删改查
如果通过上述代码来写的话,可以发现有很多的代码是重复的。所以我们使用JUnit的@Before来创建sqlSession对象以及通过@After来关闭sqlSession,释放资源。
import com.liu.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class Test1 {
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
@Before
public void init(){
//读取mybatis的配置文件得到配置文件流
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
//根据配置文件信息,创建会话工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//通过工厂得到SqlSession对象
sqlSession = sqlSessionFactory.openSession();
} catch (IOException e) {
e.printStackTrace();
}
}
//查询用户
@Test
public void test01() throws IOException {
//通过sqlSession对象执行映射文件中定义的SQL,并返回映射结果
List<User> list = sqlSession.selectList("userMapper.findAll");
System.out.println(list);
}
//添加用户
@Test
public void test02(){
User user = new User();
user.setId(10);
user.setUsername("一球");
user.setEmail("314131@dwq");
user.setPassword("131411");
user.setPhoneNum("13461460194");
int insert = sqlSession.insert("userMapper.addUser",user);
System.out.println(insert);
}
//删除用户
@Test
public void test03(){
int i = sqlSession.delete("userMapper.deleteByName", 10);
System.out.println(i);
}
//更改用户
@Test
public void test04(){
int update = sqlSession.update("userMapper.updateById", new User(3, "嘻嘻嘻嘻嘻", "dd", "ddd", "ddd"));
System.out.println(update);
}
@After
public void destory(){
//提交事务
sqlSession.commit();
//关闭sqlSession,释放资源
sqlSession.close();
}
}
<5>mybatis的Dao层实现
通过上述mybatis快速入门你会发现,我根本就没编写Dao层呀。到时候我该如何和service层进行交互呢?但这也发现了mybatis的神奇之处:mybatis不需要Dao层就可以实现增删改查。接下来让我们看一看如何使用mybatis来实现Dao层。
第一种:传统方式实现
即首先创建一个UserDao接口,在接口中编写业务方法。通过UserDaoImpl这个类来实现UserDao的方法,从而把数据返回给Service层的UserServiceImpl。
UserDao接口:
public interface UserDao {
List<User> findAll() throws IOException;
}
UserDaoImpl接口实现:
public class UserDaoImpl implements UserDao {
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");
sqlSession.close();
return userList;
}
}
第二种:代理开发模式
采用 Mybatis 的代理开发方式实现 DAO 层的开发,这种方式是我们后面进入企业的主流。
Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis 框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper 接口开发需要遵循以下规范:
1、 Mapper.xml文件中的namespace与mapper接口的全限定名相同
2、 Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
4、 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
例如:
UserDao接口:
public interface UserDao {
List<User> findAll();
}
mapper.xml映射文件:
<mapper namespace="com.liu.dao.UserDao">
<!--查询所有-->
<select id="findAll" resultType="com.liu.POJO.User">
select *from user
</select>
UserServiceImpl测试:
public class UserServiceImpl {
public static void main(String[] args) throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.findAll();
System.out.println(userList);
}
结果:
<6>mybatis的核心配置文件
点击可直接进入官网详细查看:
configuration(配置)
-
properties:用于读取properties配置文件
<!--加载属性文件--> <properties resource="jdbc.properties"/>
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql:///stu?useSSL=false&useUnicode=true&characterEconding=UTF-8&allowPublicKeyRetrieval=true jdbc.user=root jdbc.password=131411
-
settings:一般我们用来配置日志文件
配置使用log4j日志文件
#���ȼ�ΪDEBUG����־��Ϣ�����console��file������Ŀ�ĵأ�console��file�Ķ���������Ĵ��� log4j.rootLogger=DEBUG,console #����̨������������ log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #��־������� log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
-
一般用于给类起别名
<!--给包中的类注册别名--> <typeAliases> <typeAlias type="com.liu.pojo.User" alias="User"/> <typeAlias type="com.liu.pojo.Orders" alias="Orders"/> </typeAliases>
-
一般使用自带的类型处理器,但是当想对数据进行更改操作时可以更改重写类型处理器
eg:数据库中的DateTime类型想输出时为Date
-
插件有很多,可以配置一个很常用的分页查询插件
mybatis-config.xml配置如下:
<!--配置分页助手插件--> <plugins> <plugin interceptor="com.github.pagehelper.PageHelper"> <property name="dialect" value="mysql"/> </plugin> </plugins>
但是这个插件需要引入坐标
<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>
使用的时候只需要写一行代码
//设置分页相关参数 当前页+每页要显示的个数 PageHelper.startPage(1,3);
-
environments(环境配置)
-
environment(环境变量)
-
transactionManager(事务管理器)
一般我们配置的都是JDBC事务管理器
-
dataSource(数据源)
dataSource配置获取
-
<!--数据源环境--> <environments default="development"> <!--配置一个id为development的环境--> <environment id="development"> <!--使用JDBC事务--> <transactionManager type="JDBC"/> <!--数据库连接池--> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.user}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments>
-
-
这个几乎没用过
-
超级重要!!!用来加载Mapper.xml文件的!!!
<!--加载映射文件--> <mappers> <mapper resource="UserMapper.xml"/> <mapper resource="OrdersMapper.xml"/> </mappers>
<7>mybatis的mapper文件的配置
最重要的就是下面这几块了!!!
<mapper namespace="com.liu.dao.UserDao">
这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。
<!--Sql片段的抽取-->
<sql id="sqlSelect">select *from user</sql>
属性 | 描述 |
---|---|
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType | **描述将会传入这条语句的参数的类全限定名或别名。**这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
resultType | 期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。 |
resultMap | **对外部 resultMap 的命名引用。**结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。 |
resultSets | 这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。 |
eg:在mapper.xml配置文件中写入
<!--查询所有-->
<select id="findAll" resultType="com.liu.dao.User">
select *from user
</select>
属性 | 描述 |
---|---|
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType | 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
<!--插入新User-->
<insert id="addUser" parameterType="com.liu.dao.User">
insert into user values (#{id},#{username},#{email},#{password},#{phoneNum})
</insert>
属性 | 描述 |
---|---|
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType | 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
<!--更改用户信息-->
<update id="updateById" parameterType="com.liu.dao.User">
update user set username=#{username} where id=#{id}
</update>
属性 | 描述 |
---|---|
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType | 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
<!--删除用户-->
<delete id="deleteByName" parameterType="java.lang.Integer">
delete from user where id=#{id}
</delete>
默认情况下,使用 #{}
参数语法时,MyBatis 会创建 PreparedStatement
参数占位符,并通过占位符安全地设置参数(就像使用 ? 一样)。
<8>mybatis的动态SQL
动态SQL:根据不同条件拼接 SQL 语句
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。
使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。
<!--动态SQL-->
<select id="findBySome" resultType="com.liu.pojo.User" parameterType="com.liu.pojo.User">
select *from user
<where>
<if test="id!=null">
id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
<if test="password!=null">
and password=#{password}
</if>
</where>
</select>
测试:
public static void main(String[] args) throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
User user = new User();
user.setId(1);
user.setUsername("GG");
User user1 = new User();
user1.setId(2);
List<User> userList = mapper.findBySome(user);
System.out.println(userList);
List<User> bySome = mapper.findBySome(user1);
System.out.println(bySome);
sqlSession.close();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nOPOdgdj-1603351515606)(https://shop.io.mi-img.com/app/shop/img?id=shop_177e47475fd61ba1217cc93fcaeb2891.png)]
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。
<!--通过多个id查询用户数据-->
<select id="findByIds" parameterType="list" resultType="com.liu.pojo.User">
select *from user
<where>
<foreach collection="list" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
测试:
public static void main(String[] args) throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(10);
arrayList.add(1);
List<Integer> arrayList1 = new ArrayList<Integer>();
arrayList1.add(2);
arrayList1.add(4);
List<User> mapperByIds = mapper.findByIds(arrayList);
for (User mapperById : mapperByIds) {
System.out.println(mapperById);
}
List<User> userList = mapper.findByIds(arrayList1);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vjWTBTBT-1603351515608)(https://shop.io.mi-img.com/app/shop/img?id=shop_65178e8af9b99cffec88dcc3150d1888.png)]
<9>mybatis的多表查询
接下来的就是重点啦~
学会使用了简单的Crud,现在有一个问题出现在我们面前。上述说的crud都是单表的操作,那么双表的操作该如何用mybatis实现呢?
多表操作无非就是三种:一对一,一对多,多对多
一对一我们应该要如何实现呢?
这里是关于用户和订单的业务,我们要查询每个订单的用户都是谁。我们该如何去做呢?
1.这时我们需要把要查询的用户 给封装成订单的一个属性并生成get/set方法
public class Orders {
private int id;
private Date orderTime;
private int total;
//注意看这:*****把用户封装成订单的一个属性
private User user;
//重写toString方法
@Override
public String toString() {
return "Orders{" +
"id=" + id +
", orderTime=" + orderTime +
", total=" + total +
", 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 int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
//生成get/set
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
2.创建OrdersDao,并且写一个查询抽象方法。
public interface OrdersDao {
List<Orders> findAll();
}
3.在OrdersMapper.xml中进行配置:
这里的结果集我们需要自己进行封装,所以我们不能用resultType
我们需要使用resultMap进行自己封装
resultMap是Mybatis最强大的元素,它可以将查询到的复杂数据(比如查询到几个表中数据)映射到一个结果集当中。**
resultMap包含的元素:
<!--column不做限制,可以为任意表的字段,而property须为type 定义的pojo属性-->
<resultMap id="唯一的标识" type="映射的pojo对象">
<id column="表的主键字段,或者可以为查询语句中的别名字段" jdbcType="字段类型" property="映射pojo对象的主键属性" />
<result column="表的一个字段(可以为任意表的一个字段)" jdbcType="字段类型" property="映射到pojo对象的一个属性(须为type定义的pojo对象中的一个属性)"/>
<association property="pojo的一个对象属性" javaType="pojo关联的pojo对象">
<id column="关联pojo对象对应表的主键字段" jdbcType="字段类型" property="关联pojo对象的主席属性"/>
<result column="任意表的字段" jdbcType="字段类型" property="关联pojo对象的属性"/>
</association>
<!-- 集合中的property须为oftype定义的pojo对象的属性-->
<collection property="pojo的集合属性" ofType="集合中的pojo对象">
<id column="集合中pojo对象对应的表的主键字段" jdbcType="字段类型" property="集合中pojo对象的主键属性" />
<result column="可以为任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" />
</collection>
</resultMap>
<resultMap id="findUserOrder" type="Orders">
<!--手动指定字段与实体属性的映射关系
column:数据库的字段名称
property:实体的属性名称
-->
<id 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="email" property="user.email"/>
<result column="password" property="user.password"/>
<result column="phoneNum" property="user.phoneNum"/>
</resultMap>
<select id="findAll" resultMap="findUserOrder">
SELECT *,u.id usid,o.id oid
from user u,orders o
where u.id=o.uid;
</select>
上面的配置使用逻辑不好,请看下面的逻辑
<resultMap id="findUserOrder" type="Orders">
<!--手动指定字段与实体属性的映射关系
column:数据库的字段名称
property:实体的属性名称
-->
<id column="oid" property="id"/>
<result column="orderTime" property="orderTime"/>
<result column="total" property="total"/>
<!--
association标签:
property:当前实体中的属性名称(private User user)
javaType:当前实体中属性的类型(User)
-->
<association property="user" javaType="User">
<id column="uid" property="id"/>
<result column="username" property="username"/>
<result column="email" property="email"/>
<result column="password" property="password"/>
<result column="phoneNum" property="phoneNum"/>
</association>
</resultMap>
<select id="findAll" resultMap="findUserOrder">
SELECT *,u.id usid,o.id oid
from user u,orders o
where u.id=o.uid;
</select>
4.测试:
//一对一多表查询
@Test
public void TestFindAll(){
OrdersDao ordersDao = sqlSession.getMapper(OrdersDao.class);
List<Orders> ordersList = ordersDao.findAll();
for (Orders orders : ordersList) {
System.out.println(orders);
}
sqlSession.close();
}
一对多的过程与一对一的过程只差了一点点:
业务:一个User有多个Order,要查询User的Order
1.我们首先在User里要封装一个list集合,这个集合的泛型是Order类型
package com.liu.pojo;
import java.util.List;
public class User {
private int id;
private String username;
private String email;
private String password;
private String phoneNum;
//描述的是当前用户存在哪些订单
private List<Orders> orders;
//生成get/set方法
public List<Orders> getOrders() {
return orders;
}
public void setOrders(List<Orders> orders) {
this.orders = orders;
}
public User() {
}
public User(int id, String username, String email, String password, String phoneNum) {
this.id = id;
this.username = username;
this.email = email;
this.password = password;
this.phoneNum = phoneNum;
}
//重写toString方法
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", email='" + email + '\'' +
", password='" + password + '\'' +
", phoneNum='" + phoneNum + '\'' +
", orders=" + orders +
'}';
}
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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
}
2.在UserDao中创建一个方法
public interface UserDao {
List<User> findOrders();
}
3.在UserMapper.xml中进行配置
因为orders属性是一个集合,所以要用到resultMap的collection标签:
<!-- 集合中的property须为oftype定义的pojo对象的属性-->
<collection property="pojo的集合属性" ofType="集合中的pojo对象">
<id column="集合中pojo对象对应的表的主键字段" jdbcType="字段类型" property="集合中pojo对象的主键属性" />
<result column="可以为任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" />
</collection>
配置如下:
<resultMap id="userFindOrders" type="User">
<id column="uid" property="id"/>
<result column="username" property="username"/>
<result column="email" property="email"/>
<result column="password" property="password"/>
<result column="phoneNum" property="phoneNum"/>
<collection property="orders" ofType="Orders">
<id column="oid" property="id"/>
<result column="orderTime" property="orderTime"/>
<result column="total" property="total"/>
</collection>
</resultMap>
<select id="findOrders" resultMap="userFindOrders">
select *,o.id oid,u.id uid from user u,orders o where u.id=o.uid;
</select>
4.进行Test测试
//一对多查询
@Test
public void TestUserFindOrders(){
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.findOrders();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
多对多的查询与一对多几乎是一样的,除了多了一个中间表
业务介绍:我们现在有一张user表,一张role表,还有一张连接表userrole。现在我们需要知道User是什么职位的。
1.首先封装一个list集合,一个user可能有多个role,而一个role可能有多个user。所以我们需要封装一个List,泛型为role.
Role类:
package com.liu.pojo;
public class Role {
private int id;
private String rolename;
private String roleDesc;
@Override
public String toString() {
return "Role{" +
"id=" + id +
", rolename='" + rolename + '\'' +
", roleDesc='" + 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;
}
}
User类
package com.liu.pojo;
import java.util.List;
public class User {
private int id;
private String username;
private String email;
private String password;
private String phoneNum;
//描述的是当前用户存在哪些订单
private List<Orders> orders;
//查询当前用户有几个职位
private List<Role> roleList;
public List<Role> getRoleList() {
return roleList;
}
public void setRoleList(List<Role> roleList) {
this.roleList = roleList;
}
public List<Orders> getOrders() {
return orders;
}
public void setOrders(List<Orders> orders) {
this.orders = orders;
}
public User() {
}
public User(int id, String username, String email, String password, String phoneNum) {
this.id = id;
this.username = username;
this.email = email;
this.password = password;
this.phoneNum = phoneNum;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", email='" + email + '\'' +
", password='" + password + '\'' +
", phoneNum='" + phoneNum + '\'' +
", orders=" + orders +
", roleList=" + roleList +
'}';
}
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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
}
2.在UserDao创建一个方法
public interface UserDao {
List<User> findUserAndRoleAll();
}
3.在UserMapper.xml进行配置
<resultMap id="UserAndRole" type="User">
<id column="userId" property="id"/>
<result column="username" property="username"/>
<result column="email" property="email"/>
<result column="password" property="password"/>
<result column="phoneNum" property="phoneNum"/>
<collection property="roleList" ofType="Role">
<id column="roleId" property="id"/>
<result property="rolename" column="rolename"/>
<result property="roleDesc" column="roleDesc"/>
</collection>
</resultMap>
<select id="findUserAndRoleAll" resultMap="UserAndRole">
SELECT *
from `user` u,role r,userrole ur
where u.id=ur.userId
and r.id=ur.roleId;
</select>
4.测试:
//测试多对多查询
@Test
public void TestUserAndRole(){
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userAndRoleAll = mapper.findUserAndRoleAll();
for (User user : userAndRoleAll) {
System.out.println(user);
}
sqlSession.close();
}
[com.liu.dao.UserDao.findUserAndRoleAll]-==> Preparing: SELECT * from `user` u,role r,userrole ur where u.id=ur.userId and r.id=ur.roleId;
[com.liu.dao.UserDao.findUserAndRoleAll]-==> Parameters:
[com.liu.dao.UserDao.findUserAndRoleAll]-<== Total: 4
User{id=1, username='Hello!!!', email='773395726@qq.com', password='131411', phoneNum='13461460194', orders=null, roleList=[Role{id=1, rolename='院长', roleDesc='负责所有'}, Role{id=2, rolename='研究员', roleDesc='课程研发'}, Role{id=3, rolename='dd', roleDesc='dd'}]}
User{id=2, username='王爽', email='1212313131@qq.com', password='131411', phoneNum='13461460194', orders=null, roleList=[Role{id=4, rolename='辅导员', roleDesc='帮助学生进行学习、生活等等'}]}
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2145b572]
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2145b572]
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 558216562 to pool.
<10>mybatis的注解开发
每一个框架肯定是要有注解的,所以我们需要学习注解开发来简易开发流程。
-
@Insert:实现新增
-
@Update:实现更新
-
@Delete:实现删除
-
@Select:实现查询
-
@Result:实现结果集封装
-
@Results:可以与@Result 一起使用,封装多个结果集
-
@One:实现一对一结果集封装
-
@Many:实现一对多结果集封装
-
普通的增删改查
配置mybatis-config.xml核心配置文件:加载映射关系TODO
<!--加载映射关系 TODO-->
<mappers>
<package name="com.liu.dao"/>
</mappers>
基本注解的使用:
public interface UserDao {
@Select("select *from user")
List<User> findAll();
@Select("select *from user where id=#{id}")
User findById(int id);
@Update("update user set username=#{username} where id=#{id}")
int updateUser(User user);
@Insert("insert into user values (#{id},#{username},#{email},#{password},#{phoneNum})")
int insertUser(User user);
@Delete("delete from user where id=#{id}")
int deleteUser(int id);
}
基本注解测试:
public class Test01 {
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
@Before
public void init(){
//读取mybatis的配置文件得到配置文件流
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//根据配置文件信息,创建会话工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//通过工厂得到SqlSession对象
sqlSession = sqlSessionFactory.openSession(true);
} catch (IOException e) {
e.printStackTrace();
}
}
@After
public void delect(){
sqlSession.close();
}
@Test
public void save(){
User user = new User();
user.setId(13);
user.setUsername("哈哈");
user.setPassword("131411");
user.setEmail("7733333@qq.com");
user.setPhoneNum("13141414");
UserDao mapper = sqlSession.getMapper(UserDao.class);
int i = mapper.insertUser(user);
System.out.println(i);
}
@Test
public void findUser(){
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.findAll();
for (User user : userList) {
System.out.println(user);
}
}
@Test
public void findByIdUser(){
UserDao userDao = sqlSession.getMapper(UserDao.class);
User users = userDao.findById(1);
System.out.println(users);
}
@Test
public void deleteUser(){
UserDao userDao = sqlSession.getMapper(UserDao.class);
int i = userDao.deleteUser(13);
System.out.println(i);
}
@Test
public void updateUser(){
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = new User();
user.setId(1);
user.setUsername("Hello!!!");
int i = userDao.updateUser(user);
System.out.println(i);
}
}
实现复杂关系映射之前我们可以在映射文件中通过配置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 (一对一) | 代替了assocation>标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。 @One注解属性介绍: select: 指定用来多表查询的 sqlmapper使用格式:@Result(column=" “,property=”",one=@One(select="")) |
@Many (多对一) | 代替了collection标签, 是是多表查询的关键,在注解中用来指定子查询返回对象集合。 使用格式:@Result(property="",column="",many=@Many(select="")) |
public interface OrdersDao {
/* @Select(" SELECT *,u.id usid,o.id oid from user u,orders o 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 = "email",property = "user.email"),
@Result(column = "password",property = "user.password"),
@Result(column = "phoneNum",property = "user.phoneNum")
}
)*/
@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 = "com.liu.dao.UserDao.findById")
)
})
List<Orders> findAllAndUser();
}
测试:
@Test
public void findOrderAndUser(){
OrdersDao ordersDao = sqlSession.getMapper(OrdersDao.class);
List<Orders> ordersList = ordersDao.findAllAndUser();
for (Orders orders : ordersList) {
System.out.println(orders);
}
}
日志文件:
[com.liu.dao.OrdersDao.findAllAndUser]-==> Preparing: SELECT * from orders
[com.liu.dao.OrdersDao.findAllAndUser]-==> Parameters:
[com.liu.dao.UserDao.findById]-====> Preparing: select *from user where id=?
[com.liu.dao.UserDao.findById]-====> Parameters: 1(Integer)
[com.liu.dao.UserDao.findById]-<==== Total: 1
[com.liu.dao.UserDao.findById]-====> Preparing: select *from user where id=?
[com.liu.dao.UserDao.findById]-====> Parameters: 2(Integer)
[com.liu.dao.UserDao.findById]-<==== Total: 1
[com.liu.dao.OrdersDao.findAllAndUser]-<== Total: 3
Orders{id=1, orderTime=Tue Oct 20 13:00:00 CST 2020, total=3000, user=User{id=1, username='Hello!!!', email='773395726@qq.com', password='131411', phoneNum='13461460194', orders=null, roleList=null}}
Orders{id=2, orderTime=Thu Oct 01 13:00:00 CST 2020, total=2000, user=User{id=2, username='王爽', email='1212313131@qq.com', password='131411', phoneNum='13461460194', orders=null, roleList=null}}
Orders{id=3, orderTime=Fri Oct 09 13:00:00 CST 2020, total=4000, user=User{id=1, username='Hello!!!', email='773395726@qq.com', password='131411', phoneNum='13461460194', orders=null, roleList=null}}
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@be64738]
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 199640888 to pool.
UserDao中创建方法
//多表查询:一对多
@Select("select *from user")
@Results({
@Result(id = true,column = "id",property ="id" ),
@Result(column = "username",property = "username"),
@Result(column = "email",property = "email"),
@Result(column = "password",property = "password"),
@Result(column = "phoneNum",property = "phoneNum"),
@Result(
property = "orders",
column = "id",
javaType =List.class,
//这里发现我们要用OrdersDao的findById方法
many = @Many(select = "com.liu.dao.OrdersDao.findById")
)
})
List<User> findUserAndRole();
OrdersDao方法:把select *from user查到的uid传给这个方法,通过这个方法得到一些Orders,再把这些Order给封装到User的orders属性中。
@Select("select *from orders where uid=#{uid}")
List<Orders> findById();
测试:
//测试一对多
@Test
public void findUserAndRole1(){
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.findUserAndRole();
for (User user : userList) {
System.out.println(user);
}
}
测试日志结果:
[com.liu.dao.UserDao.findUserAndRole]-==> Preparing: select *from user
[com.liu.dao.UserDao.findUserAndRole]-==> Parameters:
[com.liu.dao.OrdersDao.findById]-====> Preparing: select *from orders where uid=?
[com.liu.dao.OrdersDao.findById]-====> Parameters: 1(Integer)
[com.liu.dao.OrdersDao.findById]-<==== Total: 2
[com.liu.dao.OrdersDao.findById]-====> Preparing: select *from orders where uid=?
[com.liu.dao.OrdersDao.findById]-====> Parameters: 2(Integer)
[com.liu.dao.OrdersDao.findById]-<==== Total: 1
[com.liu.dao.OrdersDao.findById]-====> Preparing: select *from orders where uid=?
[com.liu.dao.OrdersDao.findById]-====> Parameters: 3(Integer)
[com.liu.dao.OrdersDao.findById]-<==== Total: 0
[com.liu.dao.OrdersDao.findById]-====> Preparing: select *from orders where uid=?
[com.liu.dao.OrdersDao.findById]-====> Parameters: 4(Integer)
[com.liu.dao.OrdersDao.findById]-<==== Total: 0
[com.liu.dao.OrdersDao.findById]-====> Preparing: select *from orders where uid=?
[com.liu.dao.OrdersDao.findById]-====> Parameters: 10(Integer)
[com.liu.dao.OrdersDao.findById]-<==== Total: 0
[com.liu.dao.OrdersDao.findById]-====> Preparing: select *from orders where uid=?
[com.liu.dao.OrdersDao.findById]-====> Parameters: 11(Integer)
[com.liu.dao.OrdersDao.findById]-<==== Total: 0
[com.liu.dao.UserDao.findUserAndRole]-<== Total: 6
User{id=1, username='Hello!!!', email='773395726@qq.com', password='131411', phoneNum='13461460194', orders=[Orders{id=1, orderTime=Tue Oct 20 00:00:00 CST 2020, total=3000, user=null}, Orders{id=3, orderTime=Fri Oct 09 00:00:00 CST 2020, total=4000, user=null}], roleList=null}
User{id=2, username='王爽', email='1212313131@qq.com', password='131411', phoneNum='13461460194', orders=[Orders{id=2, orderTime=Thu Oct 01 00:00:00 CST 2020, total=2000, user=null}], roleList=null}
User{id=3, username='嘻嘻嘻嘻嘻', email='eeeeeeeeeeeeee', password='eeeeeeeeeeeee', phoneNum='eeeeeeeeeeeee', orders=[], roleList=null}
User{id=4, username='liuchang', email='773395726@qq.com', password='ed', phoneNum='dd', orders=[], roleList=null}
User{id=10, username='一球', email='314131@dwq', password='131411', phoneNum='13461460194', orders=[], roleList=null}
User{id=11, username='一球', email='314131@dwq', password='131411', phoneNum='13461460194', orders=[], roleList=null}
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@be64738]
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 199640888 to pool.
UserDao中创建方法
//测试多对多
@Select("select *from user")
@Results({
@Result(id = true,column = "id",property ="id" ),
@Result(column = "username",property = "username"),
@Result(column = "email",property = "email"),
@Result(column = "password",property = "password"),
@Result(column = "phoneNum",property = "phoneNum"),
@Result(
property = "roleList",
column = "id",
javaType =List.class,
many = @Many(select = "com.liu.dao.RoleDao.findByUid")
)})
List<User> findUserRoleAll();
RoleDao方法:把select *from user查到的uid传给这个方法,通过这个方法得到一些Orders,再把这些Order给封装到User的orders属性中。
public interface RoleDao {
@Select("select * from role r,userrole ur where r.id=ur.roleId and ur.userId=#{uid}")
List<Role> findByUid(int uid);
}
测试:
//测试多对多
@Test
public void findUserAndRoles(){
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.findUserRoleAll();
for (User user : userList) {
System.out.println(user);
}
}
测试日志:
[com.liu.dao.UserDao.findUserRoleAll]-==> Preparing: select *from user
[com.liu.dao.UserDao.findUserRoleAll]-==> Parameters:
[com.liu.dao.RoleDao.findByUid]-====> Preparing: select * from role r,userrole ur where r.id=ur.roleId and ur.userId=?
[com.liu.dao.RoleDao.findByUid]-====> Parameters: 1(Integer)
[com.liu.dao.RoleDao.findByUid]-<==== Total: 3
[com.liu.dao.RoleDao.findByUid]-====> Preparing: select * from role r,userrole ur where r.id=ur.roleId and ur.userId=?
[com.liu.dao.RoleDao.findByUid]-====> Parameters: 2(Integer)
[com.liu.dao.RoleDao.findByUid]-<==== Total: 1
[com.liu.dao.RoleDao.findByUid]-====> Preparing: select * from role r,userrole ur where r.id=ur.roleId and ur.userId=?
[com.liu.dao.RoleDao.findByUid]-====> Parameters: 3(Integer)
[com.liu.dao.RoleDao.findByUid]-<==== Total: 0
[com.liu.dao.RoleDao.findByUid]-====> Preparing: select * from role r,userrole ur where r.id=ur.roleId and ur.userId=?
[com.liu.dao.RoleDao.findByUid]-====> Parameters: 4(Integer)
[com.liu.dao.RoleDao.findByUid]-<==== Total: 0
[com.liu.dao.RoleDao.findByUid]-====> Preparing: select * from role r,userrole ur where r.id=ur.roleId and ur.userId=?
[com.liu.dao.RoleDao.findByUid]-====> Parameters: 10(Integer)
[com.liu.dao.RoleDao.findByUid]-<==== Total: 0
[com.liu.dao.RoleDao.findByUid]-====> Preparing: select * from role r,userrole ur where r.id=ur.roleId and ur.userId=?
[com.liu.dao.RoleDao.findByUid]-====> Parameters: 11(Integer)
[com.liu.dao.RoleDao.findByUid]-<==== Total: 0
[com.liu.dao.UserDao.findUserRoleAll]-<== Total: 6
User{id=1, username='Hello!!!', email='773395726@qq.com', password='131411', phoneNum='13461460194', orders=null, roleList=[Role{id=1, rolename='院长', roleDesc='负责所有'}, Role{id=2, rolename='研究员', roleDesc='课程研发'}, Role{id=3, rolename='dd', roleDesc='dd'}]}
User{id=2, username='王爽', email='1212313131@qq.com', password='131411', phoneNum='13461460194', orders=null, roleList=[Role{id=4, rolename='辅导员', roleDesc='帮助学生进行学习、生活等等'}]}
User{id=3, username='嘻嘻嘻嘻嘻', email='eeeeeeeeeeeeee', password='eeeeeeeeeeeee', phoneNum='eeeeeeeeeeeee', orders=null, roleList=[]}
User{id=4, username='liuchang', email='773395726@qq.com', password='ed', phoneNum='dd', orders=null, roleList=[]}
User{id=10, username='一球', email='314131@dwq', password='131411', phoneNum='13461460194', orders=null, roleList=[]}
User{id=11, username='一球', email='314131@dwq', password='131411', phoneNum='13461460194', orders=null, roleList=[]}
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@49d904ec]
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 1238959340 to pool.
Process finished with exit code 0
<11>mybatis总结
大家在开始学习mybatis插件的时候可以下载一个插件,可以通过这个插件就能像Spring查看方法的定义之处。
这算是第二遍学习mybatis了,还不错。过去学的东西忘了好多,但是学习过后,有的东西都加深了理解。以前很多不会的知识,现在懂了很多。要加油哦~接下来就要学习mybatis-plus,不知道这个难度如何。感觉这个应该和mybatis差距不大,毕竟是基于mybatis的。加油!!!