关联映射和缓存机制学习笔记

news2024/9/20 19:43:29

学习视频:4001 关联映射概述_哔哩哔哩_bilibili~4007 案例:商品的类别_哔哩哔哩_bilibili

目录

1.关联映射概述

        1.1关联映射关系

        一对一关系

        一对多关系

        多对多关系

         Java对象如何描述事物之间的关系

1.2一对一查询

        元素

a.嵌套查询方式 

b.嵌套结果方式

a.具体实现

b.具体实现

MyBatis延迟加载的配置

1.3一对多查询

 元素

a.嵌套查询方式

b.嵌套结果方式

 1.4多对多查询

        a.嵌套查询

b.嵌套结果

2.缓存机制

        2.1一级缓存

一级缓存级别                

2.2二级缓存

默认的二级缓存可实现功能

元素的属性

Cache Hit Ratio(缓存命中率)

 两个级别缓存的不同

案例:商品的类别


1.关联映射概述

        1.1关联映射关系

        在关系型数据库中,表与表之间存在着三种关联映射关系,分别为一对一关系、一对多关系和多对多关系。

        一对一关系

         一个数据表中的一条记录最多可以和另一个数据表中的一条记录相关。例如,现实生活中学生与校园卡就属于一对一的关系,一个学生只能拥有一张校园卡,一张校园卡只能属于一个学生。

        一对多关系

         主键数据表中的一条记录可以和另外一个数据表的多条记录相关。但另外一个数据表中的记录只能与主键数据表中的某一条记录相关。例如,现实中班级与学生的关系就属于一对多的关系,一个班级可以有很多学生,但一个学生只能属于一个班级。

        多对多关系

        一个数据表中的一条记录可以与另外一个数据表任意数量的记录相关,另外一个数据表中的一条记录也可以与本数据表中任意数量的记录相关。例如,现实中学生与教师属于多对多的关系,一名学生可以由多名教师授课,一名教师可以为多名学生授课。


         Java对象如何描述事物之间的关系

         数据表之间的关系实质上描述的是数据之间的关系,除了数据表,在Java中,还可以通过对象来描述数据之间的关系。通过Java对象描述数据之间的关系,其实就是使对象的属性与另一个对象的属性相互关联

                       

        一对一

A类中定义B类对象b作为属性,在B类中定义A类对象a作为属性。

        一对多

定义在A类中,定义一个B类对象的集合作为A类的属性;在B类中,定义A类对象a作为B类的属性

        多对多

在A类中定义B类类型的集合作为属性,在B类中定义A类类型的集合作为属性。


1.2一对一查询

        <association>元素

          在现实生活中,一对一关联关系是十分常见的。例如,一个人只能有一个身份证,同时一个身份证也只会对应一个人。人与身份证之间的关联关系如图。

         在MyBatis中,通过<association>元素来处理一对一关联关系。<association>元素提供了一系列属性用于维护数据表之间的关系。

        <association>元素属性

        <association>元素的配置方式

         <association>元素是<resultMap>元素的子元素,它有两种配置方式,嵌套查询方式和嵌套结果方式,下面对这两种配置方式分别进行介绍。

a.嵌套查询方式 

         嵌套查询是指通过执行另外一条SQL映射语句来返回预期的复杂类型。

<association property="card" column="card_id" 
	javaType="com.itheima.pojo.IdCard"	
	 select="com.itheima.mapper.IdCardMapper.findCodeById" />
b.嵌套结果方式

        嵌套结果是使用嵌套结果映射来处理重复的联合结果的子集。

<association property="card" 	javaType="com.itheima.pojo.IdCard">
    <id property="id" column="card_id" />
    <result property="code" column="code" />
</association>

a.具体实现
<?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为映射的根节点,用来管理DAO接口
      namespace指定DAO接口的完整类名,表示mapper配置文件管理哪个DAO接口(包.接口名)
      mybatis会依据这个接口动态创建一个实现类去实现这个接口,而这个实现类是一个Mapper对象
   -->
<mapper namespace="com.it.mapper.PersonMapper">
    <!--
          id = "接口中的方法名"
          parameterType = "接口中传入方法的参数类型"
          resultType = "返回实体类对象:包.类名"  处理结果集 自动封装
          注意:sql语句后不要出现";"号
              查询:select标签
              增加:insert标签
              修改:update标签
              删除:delete标签
      -->
    <select id="findPersonById" parameterType="Integer" 	resultMap="IdCardWithPersonResult">
        SELECT * from tb_person where id=#{id}	</select>
    <resultMap type="Person" id="IdCardWithPersonResult">
<!--         主键列使用id-->
        <id property="id" column="id" />
<!--        非主键列使用result-->
        <result property="name" column="name" />
        <result property="age" column="age" />
        <result property="sex" column="sex" />
<!--        association来配置一对一映射-->
        <association property="card" column="card_id" javaType="IdCard"
      select="com.it.mapper.IdCardMapper.findCodeById"/>
        </resultMap>

</mapper>
<?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为映射的根节点,用来管理DAO接口
      namespace指定DAO接口的完整类名,表示mapper配置文件管理哪个DAO接口(包.接口名)
      mybatis会依据这个接口动态创建一个实现类去实现这个接口,而这个实现类是一个Mapper对象
   -->
<mapper namespace="com.it.mapper.IdCardMapper">
    <!--
          id = "接口中的方法名"
          parameterType = "接口中传入方法的参数类型"
          resultType = "返回实体类对象:包.类名"  处理结果集 自动封装
          注意:sql语句后不要出现";"号
              查询:select标签
              增加:insert标签
              修改:update标签
              删除:delete标签
      -->
    <select id="findCodeById" parameterType="Integer" resultType="IdCard">
        SELECT * from tb_idcard where id=#{id}
    </select>
    
</mapper>
<?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" >
<!--配置mybatis环境-->
<configuration>
    <!--配置连接使用的相关参数
          default为默认使用的环境:development 测试环境
                                product     生产环境
      -->
    <properties resource="jdbc.properties"></properties>
    <typeAliases>
        <package name="com.it.pojo"/>
    </typeAliases>
    <environments default="development">
        <!--测试环境-->
        <environment id="development">
            <!--事务管理类型:指定事务管理的方式 JDBC-->
            <transactionManager type="JDBC"/>
            <!--数据库连接相关配置,动态获取conZ  fig.properties文件里的内容-->
            <!--数据源类型:POOLED 表示支持JDBC数据源连接池
                            UNPOOLED 表示不支持数据源连接池
                            JNDI 表示支持外部数据源连接池
              -->
            <dataSource type="POOLED">
                <!--此处使用的是MySQL数据库,使用Oracle数据库时需要修改,仔细检查各项参数是否正确,里面配置了时区、编码方式、SSL,用以防止中文查询乱码,导致查询结果为null及SSL警告等问题-->
                <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映射文件↓↓↓↓↓ 参考格式:<mapper resource="dao/UserMapper.xml"/> -->
        <mapper resource="com/it/mapper/StudentMapper.xml"></mapper>
        <mapper resource="com/it/mapper/IdCardMapper.xml"></mapper>
        <mapper resource="com/it/mapper/PersonMapper.xml"></mapper>
    </mappers>

</configuration>
    @Test
    public void  findPersonByIdTest()
    {
        // 1、通过工具类获取SqlSession对象
        SqlSession session = MyBatisUtils.getSession();
        // 2.使用MyBatis嵌套查询的方式查询id为1的人的信息
        Person person = session.selectOne("com.it.mapper.PersonMapper.findPersonById", 1);
        // 3、输出查询结果信息
        System.out.println(person);
        // 4、关闭SqlSession
        session.close();

    }


b.具体实现

    <select id="findPersonById2" parameterType="Integer" 	resultMap="IdCardWithPersonResult2">
        SELECT * ,p.id pid,
                  c.id cid
        from tb_person p,
             tb_idcard c
        where
            p.card_id=c.id
        and  p.id=#{id}

    </select>
    <resultMap type="Person" id="IdCardWithPersonResult2">
        <!--         主键列使用id-->
        <id property="id" column="pid" />
        <!--        非主键列使用result-->
        <result property="name" column="name" />
        <result property="age" column="age" />
        <result property="sex" column="sex" />
        <!--        association来配置一对一映射-->
        <association property="card"  javaType="IdCard">
        <id property="id" column="cid"></id>
        <result property="code" column="code"></result>
        </association>
    </resultMap>

    @Test
    public void  findPersonById2Test()
    {
        // 1、通过工具类获取SqlSession对象
        SqlSession session = MyBatisUtils.getSession();
       
        Person person = session.selectOne("com.it.mapper.PersonMapper.findPersonById2", 2);
        // 3、输出查询结果信息
        System.out.println(person);
        // 4、关闭SqlSession
        session.close();

    }


MyBatis延迟加载的配置

          在使用MyBatis嵌套查询方式进行MyBatis关联映射查询时,使用MyBatis的延迟加载在一定程度上可以降低运行消耗并提高查询效率。MyBatis默认没有开启延迟加载,需要在mybatis-config.xml中的<settings>元素内进行配置。

<settings>
    <!-- 打开延迟加载的开关 -->  
    <setting name="lazyLoadingEnabled" value="true" />  
    <!-- 将积极加载改为消息加载,即按需加载 -->  
    <setting name="aggressiveLazyLoading" value="false"/>  
</settings>

1.3一对多查询

   与一对一的关联关系相比,接触更多的关联关系是一对多(或多对一)。例如一个用户可以有多个订单,多个订单也可以归一个用户所有。用户和订单的关联关系如图。

 <collection>元素

        在MyBatis中,通过<collection>元素来处理一对多关联关系。<collection>元素的属性大部分与<association>元素相同,但其还包含一个特殊属性一ofType。ofType属性与javaType属性对应,它用于指定实体类对象中集合类属性所包含的元素的类型。

<collection>元素的配置方式

                <collection>元素是<resultMap>元素的子元素,<collection >元素有嵌套查询嵌套结果两种配置方式。

a.嵌套查询方式
<collection property="ordersList" 
	column="id" 
	ofType="com.itheima.pojo.Orders"
select=" com.itheima.mapper.OrdersMapper.selectOrders"/>
b.嵌套结果方式
<collection property="ordersList"ofType="com.itheima.pojo.Orders">
    <id property="id" column="orders_id" />
    <result property="number" column="number" />
</collection>
<?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为映射的根节点,用来管理DAO接口
      namespace指定DAO接口的完整类名,表示mapper配置文件管理哪个DAO接口(包.接口名)
      mybatis会依据这个接口动态创建一个实现类去实现这个接口,而这个实现类是一个Mapper对象
   -->
<mapper namespace="com.it.mapper.UsersMapper">
    <!--
          id = "接口中的方法名"
          parameterType = "接口中传入方法的参数类型"
          resultType = "返回实体类对象:包.类名"  处理结果集 自动封装
          注意:sql语句后不要出现";"号
              查询:select标签
              增加:insert标签
              修改:update标签
              删除:delete标签
      -->

    <select id="findUserWithOrdersById" parameterType="Integer" resultMap="UserWithOrdersResult">
        select *,u.id uid, o.id oid
        from tb_user u
        ,tb_orders o
        where
            u.id=o.user_id
        and
            u.id=#{id}
    </select>




    <resultMap type="Users" id="UserWithOrdersResult">
        <id column="uid" property="id" ></id>
        <result column="username" property="username"></result>
        <result column="address" property="address"></result>
        <collection property="ordersList"   javaType="list" ofType="Orders">
            <id property="id" column="oid"/>
            <result property="number" column="number"/>
        </collection>
    </resultMap>

</mapper>
 @Test
    public void findUserWithOrdersById()
    {
        SqlSession session = MyBatisUtils.getSession();
        Users  users= session.selectOne("com.it.mapper.UsersMapper.findUserWithOrdersById",1);
        System.out.println(users);
        session.close();

    }


 1.4多对多查询

订单和商品多对多关系图

        在实际项目开发中,多对多的关联关系非常常见。以订单和商品为例,一个订单可以包含多种商品,而一种商品又可以属于多个订单,订单和商品属于多对多关联关系,订单和商品之间的关联关系如图

        在数据库中,多对多的关联关系通常使用一个中间表来维护,中间表中的订单id作为外键关联订单表的id,中间表中的商品id作为外键关联商品表的id。这三个表之间的关系如图。        

        a.嵌套查询

 Orders类重写toString()方法

    @Override
    public String toString() {
        return "Orders{" +
                "id=" + id +
                ", number='" + number + '\'' +
                ", userId=" + userId +
                ", productList=" + productList +
                '}';
    }

ProductMapper.xml 

  <select id="findProductById"
            parameterType="Integer"
            resultType="product"
    >
        SELECT * FROM tb_product WHERE id IN(
            SELECT product_id FROM tb_ordersitem WHERE orders_id =#{id})

    </select>

OrdersMapper.xml

<mapper namespace="com.it.mapper.OrdersMapper">
    <!--
          id = "接口中的方法名"
          parameterType = "接口中传入方法的参数类型"
          resultType = "返回实体类对象:包.类名"  处理结果集 自动封装
          注意:sql语句后不要出现";"号
              查询:select标签
              增加:insert标签
              修改:update标签
              删除:delete标签
      -->
    <select id="findOrdersById"
        parameterType="Integer"
            resultMap="orderWithProductResult"
    >
        SELECT * FROM tb_orders WHERE id=#{id};


    </select>

    <resultMap id="orderWithProductResult" type="Orders">
        <id property="id" column="id"></id>
        <result  property="number" column="number"></result>
        <!--商品信息的映射-->
        <collection property="productList"
                    column="id"
                    javaType="list"
                    ofType="product"
        select="com.it.mapper.ProductMapper.findProductById"
        >
        </collection>

    </resultMap>


</mapper>

测试: 

    @Test
    public void findOrderWithProductById()
    {
        SqlSession session=MyBatisUtils.getSession();
        Orders orders=session.selectOne("com.it.mapper.OrdersMapper.findOrdersById",1);
        System.out.println(orders);
        session.close();

    }


b.嵌套结果

OrdersMapper.xml

<select id="findOrdersById2"
            parameterType="Integer"
            resultMap="orderWithProductResult2"
    >

        SELECT o.*,
               p.*,
               o.id oid,
               p.id pid
        FROM
            tb_orders o,
            tb_product p,
            tb_ordersitem oi
        WHERE
            o.id=oi.orders_id
          AND     p.id=oi.product_id
          AND     o.id=#{id}

    </select>

    <resultMap id="orderWithProductResult2" type="orders">
            <id column="oid" property="id"></id>
            <result property="number" column="number"></result>
            <collection property="productList"
                        javaType="list"
                        ofType="product"
            >
             <id property="id" column="pid"></id>
            <result property="name" column="name"></result>
                <result property="price" column="price"></result>
            </collection>
    </resultMap>

测试

 @Test
    public void findOrderWithProductById2()
    {
        SqlSession session=MyBatisUtils.getSession();
        Orders orders=session.selectOne("com.it.mapper.OrdersMapper.findOrdersById2",1);
        System.out.println(orders);
        session.close();

    }


2.缓存机制

        2.1一级缓存

一级缓存级别                

          MyBatis的一级缓存是SqlSession级别的缓存。如果同一个SqlSession对象多次执行完全相同的SQL语句时,在第一次执行完成后,MyBatis会将查询结果写入到一级缓存中,此后,如果程序没有执行插入、更新、删除操作,当第二次执行相同的查询语句时,MyBatis会直接读取一级缓存中的数据,而不用再去数据库查询,从而提高了数据库的查询效率。

举例

        例如,存在数据表tb_book,从表中多次查询id为1的图书信息,当程序第一次查询id为1的图书信息时,程序会将查询结果写入MyBatis一级缓存,当程序第二次查询id为1的图书信息时,MyBatis直接从一级缓存中读取,不再访问数据库进行查询。当程序对数据库执行了插入、更新、删除操作,MyBatis会清空一级缓存中的内容以防止程序误读。

查询过程

数据库准备

USE mybatis;
CREATE TABLE  tb_book( 
     id INT PRIMARY KEY AUTO_INCREMENT,
     bookName VARCHAR(255),
     price DOUBLE,
     author VARCHAR(40)   );
INSERT INTO tb_book(bookName,price,author) 
	VALUES('Java基础入门',45.0,'	传智播客高教产品研发部');
INSERT INTO tb_book(bookName,price,author) 
	VALUES('Java基础案例教程',48.0,'黑马程序员');
INSERT INTO tb_book(bookName,price,author) 
	VALUES('JavaWeb程序设计任务教程',50.0,'黑马程序员');
			

实例类

public class Book {
    private int id;
    private String bookName;
    private double price;
    private String author;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", bookName='" + bookName + '\'' +
                ", price=" + price +
                ", author='" + author + '\'' +
                '}';
    }
}

映射文件

<mapper namespace="com.it.mapper.BookMapper">
    <!--
          id = "接口中的方法名"
          parameterType = "接口中传入方法的参数类型"
          resultType = "返回实体类对象:包.类名"  处理结果集 自动封装
          注意:sql语句后不要出现";"号
              查询:select标签
              增加:insert标签
              修改:update标签
              删除:delete标签
      -->
    <select id="findBookById" parameterType="Integer"
            resultType="Book">
        SELECT * from tb_book where id=#{id}	</select>
    <!-- 根据id更新图书信息 -->
    <update id="updateBook"
            parameterType="Book">
        update tb_book set bookName=#{bookName},price=#{price}
        where id=#{id}		</update>
</mapper>

日志依赖(搞坐标)

<dependency>
   <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
   <version>1.2.17</version>
</dependency>

文件 log4j.properties

#全局日志配置
log4j.rootLogger=DEBUG, Console
#控制台输出配置
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
#日志输出级别,只展示了一个
log4j.logger.java.sql.PreparedStatement=DEBUG
 

测试

    @Test
    public void findBookByIdTest1()
    {
        SqlSession session=MyBatisUtils.getSession();
        System.out.println("******第一次查询******");
        Book book=session.selectOne("com.it.mapper.BookMapper.findBookById",1);
        System.out.println(book);
        System.out.println("******第一次查询******");
        Book book2=session.selectOne("com.it.mapper.BookMapper.findBookById",1);
        System.out.println(book2);

        session.close();

    }

MyBatis如何防止程序误读

         当程序对数据库执行了插入、更新、删除操作后,MyBatis会清空一级缓存中的内容,以防止程序误读。MyBatis一级缓存被清空之后,再次使用SQL查询语句访问数据库时,MyBatis会重新访问数据库。例如上面的例子,首先查询id为1的图书信息,然后使用更新语句对数据库中的图书信息进行更改,更改之后,再次对id为1的图书信息进行查询时,MyBatis依然会从数据库中查询。


2.2二级缓存

使用二级缓存的好处

         由4.5.1节的内容可知,相同的Mapper类,相同的SQL语句,如果SqlSession不同,则两个SqlSession查询数据库时,会查询数据库两次,这样也会降低数据库的查询效率。为了解决这个问题,就需要用到MyBatis的二级缓存。MyBatis的二级缓存是Mapper级别的缓存,与一级缓存相比,二级缓存的范围更大,多个SqlSession可以共用二级缓存,并且二级缓存可以自定义缓存资源。

执行过程

        在MyBatis中,一个Mapper.xml文件通常称为一个Mapper,MyBatis以namespace区分Mapper,如果多个SqlSession对象使用同一个Mapper的相同查询语句去操作数据库,在第一个SqlSession对象执行完后,MyBatis会将查询结果写入二级缓存,此后,如果程序没有执行插入、更新、删除操作,当第二个SqlSession对象执行相同的查询语句时,MyBatis会直接读取二级缓存中的数据。


默认的二级缓存可实现功能

  1. 映射文件中所有select语句将会被缓存。
  2. 映射文件中的所有insert、update和delete语句都会刷新缓存。
  3. 缓存会使用LRU算法回收。
  4. 没有刷新间隔,缓存不会以任何时间顺序来刷新。
  5. 缓存会存储列表集合或对象的1024个引用。
  6. 缓存是可读/可写的缓存,这意味着对象检索不是共享的,缓存可以安全的被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

<cache>元素的属性

如果需要调整级缓存的特性,可通过<cache>元素的属性来实现。

实现接口 

测试

    @Test
    public void findBookByIdTest2()
    {
         SqlSession  sqlSession1 = MyBatisUtils.getSession();
        SqlSession  sqlSession2= MyBatisUtils.getSession();
        System.out.println("******第一次查询******");
        Book book1=sqlSession1.selectOne("com.it.mapper.BookMapper.findBookById",1);
        System.out.println(book1);
        System.out.println("******第一次查询******");
        //释放sqlSession,将数据写入到二级缓存
        sqlSession1.close();
        System.out.println("第二次查询");
        Book book2=sqlSession2.selectOne("com.it.mapper.BookMapper.findBookById",1);
        System.out.println(book2);

        sqlSession2.close();

    }

注意:在第一次查询完要释放sqlSession,将数据写入到二级缓存

如果在二次查询前进行了增删改


Cache Hit Ratio(缓存命中率)

         终端用户访问缓存时,如果在缓存中查找到了要被访问的数据,就叫做命中。如果缓存中没有查找到要被访问的数据,就是没有命中。当多次执行查询操作时,缓存命中次数与总的查询次数(缓存命中次数+缓存没有命中次数)的比,就叫作缓存命中率,即缓存命中率=缓存命中次数/总的查询次数。当MyBatis开启二级缓存后,第一次查询数据时,由于数据还没有进入缓存,所以需要在数据库中查询而不是在缓存中查询,此时,缓存命中率为0。第一次查询过后,MyBatis会将查询到的数据写入缓存中,当第二次再查询相同的数据时,MyBatis会直接从缓存中获取这条数据,缓存将命中,此时的缓存命中率为0.5(1/2)。当第三次查询相同的数据,则缓存命中率为0.66666(2/3),以此类推。


 两个级别缓存的不同

MyBatis的二级缓存需要手动开启,开启二级缓存通常要完成以下两个步骤。

         与使用二级缓存前,需要在MyBatis的核心配置mybatis-config.xml文件中通过<settings>元素开启二级缓存的全局配置。

<settings>
	<setting name="cacheEnabled" value="true" />
</settings>

         开启当前Mapper的namespace下的二级缓存,可以通过MyBatis映射文件中的<cache>元素来完成。

<!-- 开启当前Mapper的namespace下的二级缓存-->
<cache>
</cache>

案例:商品的类别

        完成商品的类别案例,能够根据表1和表2在数据库分别创建一个商品表和一个商品类别表, 查询商品类别为白色家电的商品的信息

详情

数据准备

USE mybatis;
# 创建一个名称为category的表
CREATE TABLE category (
  id INT(32) PRIMARY KEY AUTO_INCREMENT,
  typename VARCHAR(40)
 );
# 插入2条数据
INSERT INTO category VALUES (1, '黑色家电');
INSERT INTO category VALUES (2, '白色家电');



# 创建一个名称为product的表
CREATE TABLE product(
	id INT PRIMARY KEY AUTO_INCREMENT,
	goodsname VARCHAR(20),
	price DOUBLE,
	typeid INT
);
INSERT INTO product VALUES(NULL,'电视机',5000,1);
INSERT INTO product VALUES(NULL,'冰箱',4000,2);
INSERT INTO product VALUES(NULL,'空调',3000,2);
INSERT INTO product VALUES(NULL,'洗衣机',2000,2);

实体类

import java.util.List;

public class Category {
    private int id;
    private String typename;
    private List<Product2> product2list;

    @Override
    public String toString() {
        return "Category{" +
                "id=" + id +
                ", typename='" + typename + '\'' +
                ", product2List=" + product2list +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTypename() {
        return typename;
    }

    public void setTypename(String typename) {
        this.typename = typename;
    }

    public List<Product2> getProduct2list() {
        return product2list;
    }

    public void setProduct2List(List<Product2> product2list) {
        this.product2list = product2list;
    }
}

 


public class Product2 {
    private int id;
    private String goodsname;
    private double price;
    private int typeid;

    @Override
    public String toString() {
        return "Product2{" +
                "id=" + id +
                ", goodsname='" + goodsname + '\'' +
                ", price=" + price +
                ", typeid=" + typeid +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getGoodsname() {
        return goodsname;
    }

    public void setGoodsname(String goodsname) {
        this.goodsname = goodsname;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public int getTypeid() {
        return typeid;
    }

    public void setTypeid(int typeid) {
        this.typeid = typeid;
    }
}

映射文件

<mapper namespace="com.it.mapper.CategoryMapper">
    <!--
          id = "接口中的方法名"
          parameterType = "接口中传入方法的参数类型"
          resultType = "返回实体类对象:包.类名"  处理结果集 自动封装
          注意:sql语句后不要出现";"号
              查询:select标签
              增加:insert标签
              修改:update标签
              删除:delete标签
      -->
    <select id="findCategoryWithProduct"
            resultMap="categoryWithProduct"

    >
        select
            p.id pid,
            p.goodsname,
            p.price,
            c.id cid,
            c.typename
        from category c,product p
        where c.id=p.typeid
        and  c.id=#{cid};

    </select>
    <resultMap id="categoryWithProduct" type="Category">
        <id column="cid" property="id"></id>
        <result  column="typename" property="typename"></result>
        <!-- 一对多映射-->
        <collection property="product2list" javaType="list" ofType="product2">

            <id column="pid" property="id"></id>
            <result column="goodsname" property="goodsname"></result>
            <result column="price" property="price"></result>
        </collection>


    </resultMap>
    
    
</mapper>

测试

     @Test
    public void findCategoryWithProductTest()
        {
            SqlSession session=MyBatisUtils.getSession();
        Category category=session.selectOne("com.it.mapper.CategoryMapper.findCategoryWithProduct",2);
        session.close();
            System.out.println(category);

        }


resultMap和下面的resultMap要对应,上面的不要手快写成resultType!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1957215.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Cache常用注解

依赖代码如下&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency> 常用注解详解 1. Cacheable 作用&#xff1a;主要用于配置方法&#xff0c;使其…

第九届全球渲染大赛来了!CG爱好者准备好了吗!

在CG界的日历上&#xff0c;二月和八月总是特别繁忙的月份。这两个月&#xff0c;全球CG艺术界最盛大的赛事——全球渲染大赛&#xff0c;都会开放报名&#xff0c;吸引着世界各地的CG艺术家和爱好者参与。备受期待的第九届全球渲染大赛&#xff0c;已经定于2024年8月3日在美国…

微信私域运营工具分享

解决微信多管理难的问题&#xff0c;多微信工作重复做&#xff0c;效率低的问题&#xff0c;防止飞单、删除客户&#xff0c;解决私域运营的难题

在双碳目标下,如何实现工厂的数字化改造升级

在"双碳"目标下&#xff0c;如何实现工厂的数字化改造升级 在“双碳”目标&#xff0c;即2030年前实现碳达峰、2060年前实现碳中和的宏伟蓝图下&#xff0c;企业作为经济社会活动的主体&#xff0c;其改造升级不仅是响应国家战略的必然要求&#xff0c;也是实现可持…

软件压力测试知识大揭秘,专业软件测评公司推荐

在信息技术迅猛发展的今天&#xff0c;软件已经成为各个行业运作的核心。有助于提升工作效率和管理水平的&#xff0c;软件的稳定性和性能也变得尤为重要。而软件压力测试&#xff0c;作为一种重要的测试手段&#xff0c;逐渐受到了更多企业的重视。 软件压力测试&#xff0c;…

【ROS 最简单教程 001/300】ROS 概念介绍

ROS&#xff1a;Robot Operating System 【适用于机器人的开源元操作系统】 ROS Plumbing Tools Capabilities Ecosystem 通讯 Plumbing ⭐ 实现ROS不同节点之间的交互工具 Tools ⭐ 工具软件包 (ROS中的开发和调试工具)&#xff0c;提供 仿真 功能&#xff1b;功能 Capabi…

图文好物和无人直播实操:定位/涨粉/养号/橱窗/作品/制作/剪辑/开播/等等

1.前言 各位小伙伴大家好&#xff0c;这里是天夏共创&#xff0c;免费分享副业/创业精品项目资源&#xff0c;打破互联网创业/副业信息壁垒&#xff0c;和您一起共享副业/创业项目资源&#xff0c;开启智能化创业/副业新时代&#xff01;致力于每天免费分享全网互联网精品VIP项…

React类组件生命周期与this关键字

类组件生命周期 参考链接 一图胜千言&#xff08;不常用的生命周期函数已隐藏&#xff09; 代码&#xff1a; //CC1.js import { Component } from "react";export default class CC1 extends Component {constructor(props) {super(props);console.log("con…

基于IDEA+Mysql+SpringBoot开发的社区养老服务管理系统

基于IDEAMysqlSpringBoot开发的社区养老服务管理系统 项目介绍&#x1f481;&#x1f3fb; node -version 14.21.3 在当前社会老龄化趋势日益加剧的背景下&#xff0c;构建一个高效、便捷的社区网养老服务管理系统显得尤为重要。本项目基于Spring Boot框架开发&#xff0c;旨…

网站打不开怎么办,收藏以备不时之需

DNS设置示范教程 部分地区有使用移动网络的小伙伴们吐槽无法访问部分网站的情况&#xff0c;同样的网站&#xff0c;使用电信和联通的用户就能正常访问。 这其实有很大几率是由于运营商的网络问题导致的&#xff0c;容易出现网站打不开的结果。 要解决移动网络无法访问的情况…

【React Hooks原理 - useTransition】

概述 在上一篇中我们介绍了useDeferredValue的基本原理&#xff0c;本文主要介绍一下useTransition这个Hook&#xff0c;之所以在这里提到useDeferredValue&#xff0c;是因为这两个Hook都是在React18引入的进行渲染优化的Hooks&#xff0c;在某些功能上是重叠的&#xff0c;主…

YOLO入门教程(一)——训练自己的模型【含教程源码 + 故障排查】

目录 引言前期准备Step1 打标训练Step2 格式转换Step3 整理训练集Step4 训练数据集4.1创建yaml文件4.2训练4.3故障排查4.3.1OpenCV版本故障&#xff0c;把OpenCV版本升级到4.0以上4.3.2NumPy版本故障&#xff0c;把NumPy降低版本到1.26.44.3.3没有安装ultralytics模块4.3.4Aria…

自闭症儿童上学指南:帮助孩子适应校园生活

在自闭症儿童成长的道路上&#xff0c;校园生活是他们融入社会、学习新知、发展社交技能的重要一步。作为星启帆自闭症儿童康复机构&#xff0c;我们深知这一过程对于孩子及其家庭而言既充满挑战也极具意义。 一、前期准备&#xff1a;建立坚实的支持体系 1. 深入了解孩子需求 …

【机器学习】梯度下降函数如何判断其收敛、学习率的调整以及特征缩放的Z-分数标准化

#引言 在机器学习中&#xff0c;特征缩放和学习率是两个非常重要的概念&#xff0c;它们对模型的性能和训练速度有显著影响。 特征缩放是指将数据集中的特征值缩放到一个固定的范围内&#xff0c;通常是在0到1之间或者标准化到均值为0、方差为1。特征缩放对于模型的训练至关重要…

Vmware安装openstack

安装虚拟机 创建完成后&#xff0c;点击开启虚拟机 稍等执行成功后 上传压缩包到指定目录。将yoga_patch.tar.gz包上传至/root目录下&#xff0c;将stack3_without_data.tar.gz包使用WinSCP上传至/opt目录下 vim run_yoga.sh #/bin/bash cd /root sudo apt-get update tar -xzv…

java面向对象总结

java面向对象篇到这里就已经结束了&#xff0c;有什么不懂的地方可以逐一进行重新观看。希望大家能够从入门到起飞。 Java面向对象基础篇综合训练&#xff08;附带全套源代码及逐语句分析&#xff09;-&#xff1e;基于javabeen Java面向对象进阶篇综合训练&#xff08;附带全…

如何跨专业通过软件设计师考试

链接&#xff1a;如何跨专业通过软件设计师-软件中级职称考试 (qq.com)

经济下行,企业还在“裁员至上”?

最近小红书、B站崩溃&#xff0c;又延伸到某云服务厂商问题频发&#xff0c;让人忍不住戏谑&#xff1a;“这算不算裁员裁到大动脉&#xff1f;” 在阿道看来&#xff0c;各大企业的裁员动作&#xff0c;绕不开的依旧是“人月神话”&#xff1a;盲目加人带来的是成本的倍增和效…

STKMATLAB 卫星编队覆盖分析纯代码实现

任务描述 设置卫星编队&#xff08;沿航迹编队&#xff0c;大斜视角&#xff0c;幅宽100km&#xff0c;下视角30&#xff0c;斜视角26&#xff09;&#xff0c;设置分析区域&#xff08;中国全境&#xff09;&#xff0c;设置FigureOfMerit&#xff08;展示覆盖率&#xff09;…

skynet 入门篇

文章目录 概述1.实现了actor模型2.实现了服务器的基础组件 环境准备centosubuntumac编译安装 ActorActor模型定义组成 Actor调度工作线程流程工作线程权重工作线程执行规则 小结 概述 skynet 是一个轻量级服务器框架&#xff0c;而不仅仅用于游戏&#xff1b; 轻量级有以下几…