高级查询
Mybatis作为一个ORM框架,也对sql的高级查询作了支持,下面我来学习mybatis中的一对一,一对多, 多对多
案例说明
此案例中的业务关系是用户,订单,订单详情,商品之间的关系,其中 一个订单属性于一个人 一个订单可以有多个订单详情 一个订单详情中包含一个商品信息 它们的关系是: 订单和人是一对一关系 订单和订单详情是一对多的关系 订单和商品是多对多的关系
表分析
导入课程资料中的数据库及实体类
业务需求
一对一查询: 查询订单,并且查询出下单人信息。
一对多查询:查询订单,查询出下单人信息并且查询出订单详情。
多对多查询:查询订单,查询出下单人信息并且查询出订单详情中的商品数据。
一对一查询
需求:查询订单,并且查询出下单人信息
sql分析
-- 查询订单,并且查询出下单人的信息 SELECT * FROM tb_user as u left join tb_order AS o ON u.id = o.user_id WHERE o.order_number = 20200921001
代码实现
@Data public class Order { private Integer id; private Long userId; private String orderNumber; //添加User对象 private User user; }
public interface UserMapper { /** * 一对一:查询订单,并且查询出下单人信息 * @param orderNumber * @return */ public Order oneToOne(@Param("orderNumber") String orderNumber); }
<!--一对一--> <resultMap id="oneToOneResultMap" type="Order" autoMapping="true"> <!--order的id--> <id property="id" column="id"/> <!--一对一映射 association: 一对一映射 property: 表示子对象的属性名 javaType: 指定子对象的类型 autoMapping: 完成子对象属性自动映射 --> <association property="user" javaType="User" autoMapping="true"> <!--user的id--> <id property="id" column="id"/> <result property="userName" column="user_name"/> </association> </resultMap> <!--一对一:查询订单,并且查询出下单人信息--> <select id="oneToOne" resultMap="oneToOneResultMap"> SELECT * FROM tb_user as u left join tb_order as o on u.id = o.user_id WHERE o.order_number = #{orderNumber} </select>
public class UserDaoTest { private UserMapper userMapper; private SqlSession sqlSession; SqlSessionFactory sqlSessionFactory; @Before public void setUp() throws Exception { //指定核心配置文件的位置 String resource = "mybatis-config.xml"; //加载核心配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); //构建sqlSessionFactory对象 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //获取SqlSession对象,SqlSession可以操作crud sqlSession = sqlSessionFactory.openSession(); //动态代理 userMapper = sqlSession.getMapper(UserMapper.class); } //一对一查询 @Test public void oneToOne(){ Order order = this.userMapper.oneToOne("20200921001"); System.out.println(order); } }
一对多查询
**一对多查询:**查询订单,查询出下单人信息并且查询出订单详情
sql分析
-- 查询订单,查询出下单人信息并且查询出订单详情。 SELECT o.id as o_id, u.id as u_id, d.id as d_id, u.user_name, o.order_number, d.total_price FROM tb_order AS o left join tb_user AS u ON o.user_id = u.id left join tb_orderdetail as d on d.order_id = o.id WHERE o.order_number = 20200921001;
代码实现
/** * 订单表 */ @Data public class Order { private Integer id; private Long userId; private String orderNumber; //一对一:添加User对象 private User user; //一对多:添加Orderdetail private List<Orderdetail> orderdetails; }
/** * 一对多:查询订单,查询出下单人信息并且查询出订单详情 * @param orderNumber * @return */ public Order oneToMany(@Param("orderNumber") String orderNumber);
<!--一对多--> <resultMap id="oneToManyResultMap" type="Order" autoMapping="true"> <!--映射order本身--> <id property="id" column="o_id"/> <!--映射user子对象--> <association property="user" javaType="User" autoMapping="true"> <id property="id" column="u_id"/> </association> <!--映射Orderdetail--> <collection property="orderdetails" javaType="List" ofType="Orderdetail" autoMapping="true"> <id property="id" column="d_id"/> </collection> </resultMap> <!--一对多:查询订单,查询出下单人信息并且查询出订单详情--> <select id="oneToMany" resultMap="oneToManyResultMap"> SELECT o.id as o_id, u.id as u_id, d.id as d_id, u.user_name, o.order_number, d.total_price FROM tb_order AS o left join tb_user AS u ON o.user_id = u.id left join tb_orderdetail as d on d.order_id = o.id WHERE o.order_number = #{orderNumber}; </select>
//一对多 @Test public void oneToMany(){ Order order = this.userMapper.oneToMany("20200921001"); System.out.println(order); }
多对多查询
**多对多查询:**查询订单,查询出下单人信息并且查询出订单详情中的商品数据
定单和商品表 是多对多的对应关系
sql分析
-- 查询订单,查询出下单人信息并且查询出订单详情中的商品数据。 SELECT o.id as o_id, u.id as u_id, d.id as d_id, i.id as i_id, u.user_name, o.order_number, d.total_price, i.item_name FROM tb_order AS o left join tb_user AS u ON o.user_id = u.id left join tb_orderdetail as d on d.order_id = o.id left join tb_item as i on d.item_id = i.id WHERE o.order_number = 20200921001
代码实现
@Data public class Orderdetail { private Integer id; private Double totalPrice; private Integer status; /*商品信息*/ private Item item; }
/** * 多对多查询:查询订单,查询出下单人信息并且查询出订单详情中的商品数据。 * @param orderNumber * @return */ public Order manyToMany(@Param("orderNumber") String orderNumber)
<!--多对多--> <resultMap id="manyToManyResultMap" type="Order" autoMapping="true"> <!--映射order本身--> <id property="id" column="o_id"/> <!--映射user子对象--> <association property="user" javaType="User" autoMapping="true"> <id property="id" column="u_id"/> </association> <!--映射Orderdetail--> <collection property="orderdetails" javaType="List" ofType="Orderdetail" autoMapping="true"> <id property="id" column="d_id"/> <!--映射item子对象--> <association property="item" javaType="Item" autoMapping="true"> <id property="id" column="i_id"/> </association> </collection> </resultMap> <!--多对多查询:查询订单,查询出下单人信息并且查询出订单详情中的商品数据。--> <select id="manyToMany" resultMap="manyToManyResultMap"> SELECT o.id as o_id, u.id as u_id, d.id as d_id, i.id as i_id, u.user_name, o.order_number, d.total_price, i.item_name FROM tb_order AS o left join tb_user AS u ON o.user_id = u.id left join tb_orderdetail as d on d.order_id = o.id left join tb_item as i on d.item_id = i.id WHERE o.order_number = #{orderNumber} </select>
//多对多 @Test public void manyToMany(){ Order order = this.userMapper.manyToMany("20200921001"); System.out.println(order); }
ResultMap的继承
回顾以上多表映射中resultMap映射中其实有 一对一,一对多,多对多中都有一对一对映射很重复的,每一次都需要写,不好,其实我们可以把相同的一对一映射进行抽取,然后再继承过来。
代码实现
分页插件
PageHelper分页插件
Mybatis的plugin实现原理
添加依赖
<!--分页--> <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>
配置插件
<!--分页插件--> <plugins> <!-- com.github.pagehelper为PageHelper类所在包名 --> <plugin interceptor="com.github.pagehelper.PageHelper"> <!--数据库方言--> <property name="dialect" value="mysql"/> <!-- 设置为true时,使用RowBounds分页会进行count查询 --> <property name="rowBoundsWithCount" value="true"/> </plugin> </plugins> 5.x后续方案 <plugin interceptor="com.github.pagehelper.PageInterceptor"> <property name="helperDialect" value="mysql"/> <property name="reasonable" value="true"/> </plugin>
实现分页
/** * 分页查询 * @return */ public List<User> queryAll();
<!--分页查询--> <select id="queryAll" resultType="User"> select * from tb_user </select>
//分页查询 @Test public void queryAll(){ //实现分页 参数1:当前页(从1开始) 参数2:显示多少条 PageHelper.startPage(2, 2); //你只管查询所有,如何分页交给 PageHelper.startPage(2, 2);完成 List<User> users = this.userMapper.queryAll(); for (User user : users) { System.out.println(user); } //获取更多的分页信息 PageInfo<User> pageInfo = new PageInfo<User>(users); System.out.println("当前页:" + pageInfo.getPageNum()); System.out.println("当前页显示的条数:" + pageInfo.getPageSize()); System.out.println("总页码:" + pageInfo.getPages()); System.out.println("最后一页:" + pageInfo.getLastPage()); System.out.println("分页相关的信息:" + pageInfo.getList()); System.out.println("分页总条数:" + pageInfo.getTotal()); }
懒加载(延迟加载)
思考问题?
一对多:我们查询用户时,要不要把关联的订单查询出来
多对一:我们查询订单时,要不要把关联的用户查询出来
什么是懒加载?
就是在需要它的时候才加载,不需要的话就不加载
使用场景
一对多,多对多 通常采用延迟加载
一对一,多对一 通常采用立即加载
配置懒加载
<settings> <!-- lazyLoadingEnabled:延迟加载启动,默认是false 相当于是否开启延迟加载 --> <setting name="lazyLoadingEnabled" value="true" /> <!--aggressiveLazyLoading:积极的懒加载,falsed话按需加载,默认是true --> <setting name="aggressiveLazyLoading" value="false"></setting> </settings>
一对多 collection延迟加载
一对多:我们查询用户时,要不要把关联的订单查询出来
@Data public class User { private Long id; private String userName; private String password; private String name; private Integer age; private Integer sex; private Date birthday; private Date created; private Date updated; /*在一方添加多方对象*/ private List<Order> orders; }
public List<User> queryAllUser(Integer id); public Order queryOrderById(Integer id);
<resultMap id="userByIdMap" type="User" autoMapping="true"> <!--一对多,延迟加载--> <collection property="orders" column="id" select="cn.yanqi.mapper.UserMapper.queryOrderById" ofType="Order" autoMapping="true"/> </resultMap> <select id="queryAllUser" resultMap="userByIdMap"> select * from tb_user where id = #{id} </select> <select id="queryOrderById" resultType="Order"> select * from tb_order where id = #{id} </select>
@Test public void queryUserById(){ List<User> list = this.userMapper.queryAllUser(2); // list.forEach(System.out::println); 需要时才会加载order }
多对一 association 延迟加载
多对一:我们查询订单时,要不要把关联的用户查询出来
@Data public class Order { private Integer id; private Long userId; private String orderNumber; //一对一:添加User对象 private User user; //一对多:添加Orderdetail private List<Orderdetail> orderdetails; }
public Order queryOrderById2(Integer id);
<resultMap id="OrderResultMap" type="Order" autoMapping="true"> <id column="id" property="id"/> <!--多对一:延迟加载 --> <association property="user" javaType="User" column="id" select="queryUserById" fetchType="lazy" autoMapping="true"/> </resultMap> <!--order查询--> <select id="queryOrderById2" resultMap="OrderResultMap"> select * from tb_order where id = #{id} </select> <!--user查询--> <select id="queryUserById" resultType="User"> select * from tb_user where id = #{id} </select>
@Test public void queryOrderById2(){ Order order = this.userMapper.queryOrderById2(2); User user = order.getUser();//需要时才加载user System.out.println(user); }
逆向工程
我们之前都是根据数据库中表的字段来完成实体类的书写,再mapper接口和对应的sql映射文件,这此常规的操作是不是很重复,其实这些东西可以自动生成。以下就是告拆大家如何快速实现CRUD操作:
mybatis-generator
添加插件
- pom.xml
<build> <plugins> <!-- mybatis代码生成插件 --> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.2</version> <configuration> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.16</version> </dependency> </dependencies> </plugin> </plugins> </build>
配置文件
- datasource.properties
db.driverClassName=com.mysql.jdbc.Driver db.url=jdbc:mysql:///mybatis?characterEncoding=utf-8 db.username=root db.password=root
- generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <!--导入属性配置--> <properties resource="datasource.properties"></properties> <!-- 指定数据库驱动的jdbc驱动jar包的位置 --> <!--<classPathEntry location="${db.driverLocation}" />--> <!-- context 是逆向工程的主要配置信息 --> <!-- id:起个名字 --> <!-- targetRuntime:设置生成的文件适用于那个 mybatis 版本 --> <context id="default" targetRuntime="MyBatis3"> <!--optional,旨在创建class时,对注释进行控制--> <commentGenerator> <property name="suppressDate" value="true" /> <!-- 是否去除自动生成的注释 true:是 : false:否 --> <property name="suppressAllComments" value="true" /> </commentGenerator> <!--jdbc的数据库连接--> <jdbcConnection driverClass="${db.driverClassName}" connectionURL="${db.url}" userId="${db.username}" password="${db.password}"> </jdbcConnection> <!--非必须,类型处理器,在数据库类型和java类型之间的转换控制--> <javaTypeResolver> <!-- 默认情况下数据库中的 decimal,bigInt 在 Java 对应是 sql 下的 BigDecimal 类 --> <!-- 不是 double 和 long 类型 --> <!-- 使用常用的基本类型代替 sql 包下的引用类型 --> <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- targetPackage:生成的实体类所在的包 --> <!-- targetProject:生成的实体类所在的硬盘位置 --> <javaModelGenerator targetPackage="cn.yanqi.pojo" targetProject=".\src\main\java"> <!-- 是否允许子包 --> <property name="enableSubPackages" value="false" /> <!-- 是否对modal添加构造函数 --> <property name="constructorBased" value="true" /> <!-- 是否清理从数据库中查询出的字符串左右两边的空白字符 --> <property name="trimStrings" value="true" /> <!-- 建立modal对象是否不可改变 即生成的modal对象不会有setter方法,只有构造方法 --> <property name="immutable" value="false" /> </javaModelGenerator> <!-- targetPackage 和 targetProject:生成的 mapper 文件的包和位置 --> <sqlMapGenerator targetPackage="mappers" targetProject=".\src\main\resources"> <!-- 针对数据库的一个配置,是否把 schema 作为字包名 --> <property name="enableSubPackages" value="false" /> </sqlMapGenerator> <!-- targetPackage 和 targetProject:生成的 interface 文件的包和位置 --> <javaClientGenerator type="XMLMAPPER" targetPackage="cn.yanqi.dao" targetProject=".\src\main\java"> <!-- 针对 oracle 数据库的一个配置,是否把 schema 作为字包名 --> <property name="enableSubPackages" value="false" /> </javaClientGenerator> <table tableName="tb_user" domainObjectName="User" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> <table tableName="tb_order" domainObjectName="Order" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> <table tableName="tb_orderdetail" domainObjectName="Orderdetail" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> <table tableName="tb_item" domainObjectName="Item" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> </context> </generatorConfiguration>
IDEA插件代码生成器
Easy Code的代码生成器,从实体类到controller service dao sql都给生成好,很方便
IDEA连接数据库
下载插件
生成代码
false">