目录
之前使用mybatis开发使用的都是xml开发方式,会产生很多的xml文件,使得项目臃肿。
mybatis提供了xml实现的增删改查对应的注解开发,分别为@Select、@Delete、@Insert、@Update。还有操作参数的@Param注解。
通过一个实例来对mybatis注解开发做解释。
通过对学生表单表操作增删改查。
1、准备工作
下面的表和数据都使用数据库管理工具来实现,我用的是Navicat数据库管理工具,大家可以使用其他的数据库管理工具,去百度一下就可以找到安装和使用教程了。
创建学生表并添加几条数据
创建工程
导入相关依赖坐标
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
</dependencies>
创建存放连接数据库信息db.properties文件
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/mybatis01test
db.username=root
db.password=123456
创建核心配置文件
<?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>
<!--
mybatis可以使用properties来引入外部properties配置文件的内容
resource引入类路径下的配置文件,
url引入网络路径或磁盘路径下的资源
-->
<properties resource="db.properties"></properties>
<!--
typeAliases标签可以定义pojo类的别名,有了这一步,在mapper.xml文件中的增删改查标签中定义返回类型resultType和传入参数类型parameterType时,可以省去把包全路径都写,只写类名即可
typeAliases标签中很重要的两个子标签,package和typeAlias:
package:可以以表路径来定义类的别名,不用每个类都来定义一个别名,比较实用
typeAlias:要每个类都要定义一个别名,比较繁琐,不推荐
-->
<typeAliases>
<package name="com.ltc.pojo"></package>
</typeAliases>
<!-- environments标签中就是 用来定义数据库的连接信息了,这里 是通过上面的properties标签加载进来的db.properties文件,在从文件中获取文件信息配置到property属性中去
这个标签内可以定义多个数据库连接,default属性是指定默认使用
-->
<environments default="development">
<!-- environment标签就是我们真正的定义数据库连接信息了,id就是表示命名空空,environments标签中的default属性就是用来指定这个id的值,来指定我们要使用那个数据库连接 -->
<environment id="development">
<!-- transactionManager是定义事务管理方式,这里用的是jdbc的事务管理方式 ,也可以使用其他的,自己学习去吧 -->
<transactionManager type="JDBC"/>
<!-- dataSource这个标签是指定使用的数据源,我觉得也可以说是数据库连接池,不知道这样理解是否合理,数据库连接池 国货之光 德鲁伊连接池,建议学学 -->
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
<!-- mapper标签主要是将定义的mapper.xml文件注册到核心配置文件中,让程序运行时能通过这里能找到定义的映射文件
如果没有这一步,则会报错,慎重!
-->
<mappers>
<!--加载映射文件或者sql接口-->
</mappers>
</configuration>
创建一个工具类,来获取SqlSession对象
package com.ltc.util;
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 java.io.IOException;
import java.io.Reader;
public class SqlSessionUtil {
private static SqlSessionFactory sessionFactory=null;
static {
try {
Reader resourceAsReader = Resources.getResourceAsReader("mybatis-config.xml");
sessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static SqlSession getSqlSession(){
return sessionFactory.openSession(true);
}
}
接下来就可以创建表与java类的映射类了,创建Student类
package com.ltc.pojo;
import lombok.Data;
@Data
public class Student {
private Integer id;
private String sno;
private String sname;
private Integer age;
private String sex;
}
接下来就是最关键的注解开发的mybatis操作数据库的时候了,创建一个StudentDao接口,这个接口要在mybatis-config.xml文件中的<mapper>属性中加入一下,让框架能找到这个接口:
<mappers>
<mapper class="com.ltc.dao.StudentDao"></mapper>
</mappers>
以上就做好准备工作了,接下就是写操作数据库的sql语句了。
@Select注解
@Select注解用于映射查询语句,相等于xml文件中的<select>元素。
上代码:
package com.ltc.dao;
import com.ltc.pojo.Student;
import org.apache.ibatis.annotations.Select;
public interface StudentDao {
//通过id查找学生
@Select("select * from student where id=#{id}")
Student findStudentById(int id);
}
测试:
import com.ltc.dao.StudentDao;
import com.ltc.pojo.Student;
import com.ltc.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class AnnoStudentTest {
@Test
public void findStudentByIdTest(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student = studentDao.findStudentById(1);
System.out.println(student);
sqlSession.close();
}
}
@Insert注解
@Insert注解是用来做向数据库中插入数据的,相当于xml文件中的<insert>元素。
package com.ltc.dao;
import com.ltc.pojo.Student;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
public interface StudentDao {
//向数据库表student表中插入学生信息
@Insert("insert into student(sno,sname,age,sex) value (#{sno},#{sname},#{age},#{sex})")
int addStudent(Student student);
}
测试:
import com.ltc.dao.StudentDao;
import com.ltc.pojo.Student;
import com.ltc.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class AnnoStudentTest {
@Test
public void addStudentTest(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student = new Student();
student.setSno("202214");
student.setSname("王五");
student.setAge(58);
student.setSex("男");
int i = studentDao.addStudent(student);
System.out.println(i);
sqlSession.close();
}
}
@Update注解
@Update注解是做数据库表数据更新的,相当于xml文件中的<update>元素。
package com.ltc.dao;
import com.ltc.pojo.Student;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
public interface StudentDao {
//通过id查找,更新数据
@Update("update student set sno=#{sno},sname=#{sname},age=#{age},sex=#{sex} where id=#{id}")
Integer updateStudent(Student student);
}
测试:
import com.ltc.dao.StudentDao;
import com.ltc.pojo.Student;
import com.ltc.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class AnnoStudentTest {
@Test
public void updateStudentTest(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student = new Student();
student.setId(2);
student.setSno("202312");
student.setSname("lisi");
student.setAge(26);
student.setSex("男");
Integer i = studentDao.updateStudent(student);
System.out.println(i);
sqlSession.close();
}
}
@Delete注解
@Delete注解是做数据库表数据删除的,相等于xml中的<delete>元素。
package com.ltc.dao;
import com.ltc.pojo.Student;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
public interface StudentDao {
//通过id删除数据
@Delete("delete from student where id=#{id}")
Integer delStudent(Integer id);
}
测试:
import com.ltc.dao.StudentDao;
import com.ltc.pojo.Student;
import com.ltc.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class AnnoStudentTest {
@Test
public void delStudentTest(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Integer i = studentDao.delStudent(2);
System.out.println(i);
sqlSession.close();
}
}
@Param注解
@Param注解的功能是指定sql语句的参数,常常用在sql语句中参数较多的情况。
@Update("update student set sno=#{sno},sname=#{sname},age=#{age},sex=#{sex} where id=#{id}")
Integer updateStudent1(@Param("id") Integer id,@Param("sno") String sno,@Param("sname") String sname,@Param("age") Integer age,@Param("sex") String sex);
使用@Param注解分别将方法中的各个参数和sql语句的字段一一对应,@Param注解将id、sno参数值映射到#{id}和#{sno}中。
package com.ltc.dao;
import com.ltc.pojo.Student;
import org.apache.ibatis.annotations.*;
public interface StudentDao {
@Update("update student set sno=#{sno},sname=#{sname},age=#{age},sex=#{sex} where id=#{id}")
Integer updateStudent1(@Param("id") Integer id,@Param("sno") Integer sno,@Param("sname") Integer sname,@Param("age") Integer age,@Param("sex") Integer sex);
}
测试:
import com.ltc.dao.StudentDao;
import com.ltc.pojo.Student;
import com.ltc.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class AnnoStudentTest {
@Test
public void updateStudent1Test(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Integer i = studentDao.updateStudent1(1, "2023112", "zhangsan", 28, "男");
System.out.println(i);
sqlSession.close();
}
}
在使用了注解开发对数据库表中的数据进行增删改查后,你会发现看着很简单,确实,只做单表的操作确实是简单的,但是在开发中对单表进行操作其实不是很多,对于多表的操作还是是比较难的,接下来就讲解一下基于注解开发多表关联操作。
基于注解的关联查询
在开发中对于查询是出现最多的,而多表关联查询就更多了,相对于单表操作。
而多表关联查询关系可以分为一对一、一对多、多对多三种。
MyBatis提供了@Results、@Result、@One和@Many等注解来实现表之间的关联查询。
还是通过案例来体验一下吧。
在下面的案例中就使用用户、用户身份证、购物订单、商品实体来操作。
一对一关系:用户、用户身份证
一对多关系:用户、订单
多对多关系:购物订单、商品
准备工作:
创建表以上说的实体对应的商品
用户表
身份证表
订单表
商品表
订单表和商品表多对多的中间表
在pom.xml文件中引入相关依赖坐标
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
</dependencies>
在resources文件下创建核心配置文件mybatis-config.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>
<!--
mybatis可以使用properties来引入外部properties配置文件的内容
resource引入类路径下的配置文件,
url引入网络路径或磁盘路径下的资源
-->
<properties resource="db.properties"></properties>
<!--
typeAliases标签可以定义pojo类的别名,有了这一步,在mapper.xml文件中的增删改查标签中定义返回类型resultType和传入参数类型parameterType时,可以省去把包全路径都写,只写类名即可
typeAliases标签中很重要的两个子标签,package和typeAlias:
package:可以以表路径来定义类的别名,不用每个类都来定义一个别名,比较实用
typeAlias:要每个类都要定义一个别名,比较繁琐,不推荐
-->
<typeAliases>
<package name="com.ltc.pojo"></package>
</typeAliases>
<!-- environments标签中就是 用来定义数据库的连接信息了,这里 是通过上面的properties标签加载进来的db.properties文件,在从文件中获取文件信息配置到property属性中去
这个标签内可以定义多个数据库连接,default属性是指定默认使用
-->
<environments default="development">
<!-- environment标签就是我们真正的定义数据库连接信息了,id就是表示命名空空,environments标签中的default属性就是用来指定这个id的值,来指定我们要使用那个数据库连接 -->
<environment id="development">
<!-- transactionManager是定义事务管理方式,这里用的是jdbc的事务管理方式 ,也可以使用其他的,自己学习去吧 -->
<transactionManager type="JDBC"/>
<!-- dataSource这个标签是指定使用的数据源,我觉得也可以说是数据库连接池,不知道这样理解是否合理,数据库连接池 国货之光 德鲁伊连接池,建议学学 -->
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
<!-- mapper标签主要是将定义的mapper.xml文件注册到核心配置文件中,让程序运行时能通过这里能找到定义的映射文件
如果没有这一步,则会报错,慎重!
-->
<mappers>
</mappers>
</configuration>
创建一个工具类来获取SqlSession连接数据库会话对象。
package com.ltc.util;
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 java.io.IOException;
import java.io.Reader;
public class SqlSessionUtil {
private static SqlSessionFactory sessionFactory=null;
static {
try {
Reader resourceAsReader = Resources.getResourceAsReader("mybatis-config.xml");
sessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static SqlSession getSqlSession(){
return sessionFactory.openSession(true);
}
}
创建和表做映射的实体类,基本都是一个标一个映射实体类。
用户实体类:
package com.ltc.pojo;
import lombok.Data;
import java.util.List;
//使用到lombok技术,可以省到大量的get、set、构造器、toString方法的编写
@Data
public class Users {
private Integer id;
private String username;
private String address;
private List<Orders> orderList;
}
身份证实体类:
package com.ltc.pojo;
import lombok.Data;
@Data
public class IdCard {
private Integer id;
private Integer code;//身份证号
}
订单实体类:
package com.ltc.pojo;
import lombok.Data;
import java.util.List;
//使用到lombok技术,可以省到大量的get、set、构造器、toString方法的编写
@Data
public class Orders {
private Integer id;
private String number;//订单号
private List<Product> products;//商品集合
}
商品实体类:
package com.ltc.pojo;
import lombok.Data;
import java.util.List;
@Data
public class Product {
private Integer id;
private String pName;
private double price;
private List<Orders> ordersList;//订单集合
}
到这了准备工作就完成了,接下来就只用关注写sql语句了。
1、一对一查询
在mybatis中使用注解开发操作一对一查询使用@One注解,相等于xml中的<assocation>元素。
通过案例来实现一下,如一个用户只有一个身份证号,一个身份证号只属于一个用户,这就是一对一的关系。
在项目的dao包下创建CardDao接口,在CardDao接口中创建findCardById方法
package com.ltc.dao;
import com.ltc.pojo.IdCard;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
public interface CardDao {
@Select("select * from tb_card where id=#{id}")
IdCard findCardById(@Param("id") Integer id);
}
在项目的dao包下创建CardDao接口,在UserDao接口中创建findUserById方法
package com.ltc.dao;
import com.ltc.pojo.Users;
import org.apache.ibatis.annotations.*;
public interface UserDao {
@Select("select * from tb_user where id=#{id}")
@Results({
@Result(id = true,property = "id",column = "id"),
@Result(property = "username",column = "username"),
@Result(property = "address",column = "address"),
@Result(property = "idCard",column = "card_id",one = @One(select = "com.ltc.dao.CardDao.findCardById"))
})
Users findUserById(@Param("id") Integer id);
}
这些接口需要在核心配置文件mybatis-config.xml文件中的<mapper>元素中注册
<mappers>
<mapper class="com.ltc.dao.UserDao"></mapper>
<mapper class="com.ltc.dao.CardDao"></mapper>
</mappers>
测试:
import com.ltc.dao.UserDao;
import com.ltc.pojo.Users;
import com.ltc.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class UserTest {
@Test
public void findUserByIdTest(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
Users user = userDao.findUserById(2);
System.out.println(user);
sqlSession.close();
}
}
2、一对多查询
在mybatis中我们可以使用@Many注解来实现一对多操作数据库表。
一对多我们可以使用用户和订单来进行演示。
一个用户可以有多个订单,而一个订单只属于一个用户。
在dao 包下创建OrderDao接口,在接口中创建findOrderById方法
package com.ltc.dao;
import com.ltc.pojo.Orders;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface OrderDao {
@Select("select * from tb_orders where id=#{id}")
Orders findOrderById(@Param("id") Integer id);
}
因为实例中的表字段名和实体类的属性名一致,所以不用做太多的表字段和类属性之间的映射。
在原理的UserDao 接口中创建findUserById1方法类做一对多的表查询就可以。
package com.ltc.dao;
import com.ltc.pojo.Users;
import org.apache.ibatis.annotations.*;
public interface UserDao {
@Select("select * from tb_user where id=#{id}")
@Results({
@Result(property = "orderList",column = "id",many = @Many(select = "com.ltc.dao.OrderDao.findOrderById"))
})
Users findUserById1(@Param("id") Integer id);
}
测试:
import com.ltc.dao.UserDao;
import com.ltc.pojo.Users;
import com.ltc.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class UserTest {
@Test
public void findUserById1Test(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
Users user = userDao.findUserById1(2);
System.out.println(user);
sqlSession.close();
}
}
3、多对多查询
在数据库中,表之间多对多之间的关系需要一个中间表来维护,就是在做准备工作是创建表时创建的中间表。在中间表中分别加入两个表的主键,来做为中间表的外键连接到两个表的主键作为表建立连接关系。
而在java实体类中需要在两个实体类中分别加入对方类的一个集合来映射多对多的关系,可以看准备工作中的Orders类和Product类中的集合类型。
在到中创建ProductDao接口,在接口中创建findProductById方法
在dao包下创建ProductDao接口,在接口中创建findProductById方法
package com.ltc.dao;
import com.ltc.pojo.Product;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface ProductDao {
@Select("select * from tb_product where id in(select p_id from tb_product_order where o_id=#{id})")
@Results({
@Result(id = true,property = "id",column = "id"),
@Result(property = "pName",column = "p_name")
})
List<Product> findProductById(@Param("id") Integer id);
}
在dao包下的OrderDao包下创建findOrderById1方法
package com.ltc.dao;
import com.ltc.pojo.Orders;
import org.apache.ibatis.annotations.*;
public interface OrderDao {
@Select("select * from tb_orders where id=#{id}")
@Results({
@Result(id = true,property = "id",column = "id"),
@Result(column = "id",property = "products",many = @Many(
select = "com.ltc.dao.ProductDao.findProductById"
))
})
Orders findOrderById1(@Param("id") Integer id);
}
测试:
import com.ltc.dao.OrderDao;
import com.ltc.pojo.Orders;
import com.ltc.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class OrderTest {
@Test
public void findOrderById1Test(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
OrderDao orderDao = sqlSession.getMapper(OrderDao.class);
Orders order = orderDao.findOrderById1(1);
System.out.println(order);
sqlSession.close();
}
}
4、调用其他接口时的参数传递(重点理解)
一对一、一对多和多对多查询对于参数传递我觉得还是的好好理解一下,不然搞不懂调用其他接口的方法时应该给这个被调用的接口中的方法对应的sql语句中应该传入那个参数,你是无法理解使用关联查询接口调用时的参数传递。
我的理解:就拿最难的多对多的关联查询来讲解把,在多对多的关联查询中,被调用的接口方法中是需要调用它的方法给 它一个参数的,如:在通过id查询订单信息和订单中所对应的商品信息时
@Select("select * from tb_product where id in(select p_id from tb_product_order where o_id=#{id})")
@Results({
@Result(id = true,property = "id",column = "id"),
@Result(property = "pName",column = "p_name")
})
List<Product> findProductById(@Param("id") Integer id);
你会发现在方法中是需要传给它一个id参数的,这时你看到会想多对多查询时,被调用的方法的参数要怎么传参数值,到底传那个参数值给它呢的疑问了,不急,下面我就详细讲解一下:
这时我们就得知道一下订单表、商品表和他们做连接的中间表的表结构了。
在tb_orders订单表中的id字段是和tb_product_order中间表 的o.id是对应的,tb_product_order中间表 的o.id是作为tb_orders表中的主键id的外键,它们的值也是一一对应的。
而tb_product表也是和tb_orders表同理。
所以在查询那个订单信息和订单所对应的商品信息时,我们肯定是先通过查询订单表的信息,在通过订单id来到中间表tb_product_order表中找到o_id字段信息记录,在可以找到o_id记录所对应的p_id的字段值,就可以通过p_id到商品表中找到相应的商品信息了。
接下来解要对应到mybatis代码中去了。用一个图来画出参数传递方向吧,用图理解可能容易点。
上面的图是我为了理解多对多关联查询的参数传递的,一对多和一对多原理大致相同。