MyBatis无敌详细版

一、MyBatis介绍


MyBatis是基于iBatis的一个持久层ORM框架,主要用于实现持久化,将瞬时状态的数据转换为持久状态的过程称为持久化过程,完成持久化的技术比较多,如jdbc(基础操作),ORM框架

ORM:对象关系映射,通过Java对象与数据库关系表的映射实现数据的持久化,在操作数据库时需要使用SQL语句并对结果进行封装,而ORM框架要达到的效果是简化数据库的开发,让数据库操作的过程更加自动化
image-20210123155954100

ORM框架:Hibernate,MyBatis,JPA

Hibernate:是一个完全的ORM框架,在Hibernate中可以不编写任何SQL语句实现对数据库的操作,在使用Hibernate操作时如果需要对SQL语句进行优化,调整则较麻烦,Hibernate的学习成本较高,目前在使用时Hibernate主要集中在项目需求变化不大场景使用

MyBatis:不完全的ORM框架,在MyBatis中需要使用大量的SQL语句来实现数据库操作,由于MyBatis中需要开发人员编写SQL语句,则对SQL的优化、调整变的就极为简单,MyBatis比较适合应用需求变化较多的应用场景,MyBatis学习成本比较低

MyBatis使用步骤:
1.在pom.xml中加入mybatis依赖
2.创建mybatis的核心配置文件(文件名任意)
3.根据表创建对应的实体对象
4.创建操作接口
5.根据操作接口创建Mapper映射文件
6.编写测试类

1.mybatis依赖

      <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>

2.配置MyBatis的核心配置文件

​ 在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">

<!--MyBatis的核心配置文件-->
<configuration>
    <!--
        environments:用于配置MyBatis的环境,在其标签体中可以配置多套MyBatis环境
        default:用于指定当前应用所使用的mybatis环境
    -->
    <environments default="development">
        <!--
            environment:配置一套具体的mybatis环境,一般情况下一个数据库对象一个mybatis环境
            id:当前mybatis环境的唯一表示,通过该唯一标识可以获得当前mybatis环境
        -->
        <environment id="development">
            <!--
                transactionManager:配置当前所使用的事务管理器
                type:指定要使用的事务类型,它执行两种事务类型:
                        1.JDBC:表示使用jdbc连接进行事务管理
                        2.MANAGED:表示使用外部容器对事务进行管理(不推荐)
            -->
            <transactionManager type="JDBC"/>
            <!--
                dataSource:配置数据源
                    type:指定数据源类型,包含三种数据源类型:
                        1.UNPOOLED:不使用数据库连接池方式获取
                        2.POOLED:使用数据库连接池方式
                        3.JNDI:由服务器提供的数据源,程序通过JNDI访问外部数据源
            -->
            <dataSource type="POOLED">
                <!--配置数据库及数据库连接池相关属性-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/goods"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <!--配置SQL映射文件-->
    <mappers>
        <mapper resource="mapper/GoodsMapper.xml"/>
    </mappers>
</configuration>

3.实体对象

public class Goods {
    
    
    private Integer g_id;
    private String g_name;
    private Double g_price;
    private Date g_date;
    public Goods(){
    
    }
//记得生成set get方法 构造方法
}

4.配置映射文件

<?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">
<!--
    SQL映射文件,在其中可以配置
        1.SQL映射
        2.对象与表中字段的映射
        3.对象与对象之间的关联映射
    namespace:指定当前映射的命名空间(是一个虚拟路径)
    为了使用MyBatis的动态代理功能来简化持久层操作,前提条件:
        1.namespace的值必须为要操作的Dao接口的完整路径
        2.数据库操作标签的id必须为Dao接口中方法的方法名

-->
<mapper namespace="com.jiazhong.Dao.GoodsDao">
    <!--
        SQL映射
        select标签表示查询SQL语句
        id:当前SQL映射的唯一表示
        resultType:当前SQL语句查询结果中单条数据要封装的Java对象(使用实体类的完整路径名)
        parameterType:指定SQL映射中需要的参数(可以省略)
    -->
    <select id="queryGoods" resultType="com.jiazhong.model.Goods">
        select * from tbl_goods
    </select>
    <!--
        当SQL语句中只需要一个参数时,参数名可以任意,通过#{
    
    name}占位符来表示
    -->
    <select id="queryGoodsById"  resultType="com.jiazhong.model.Goods">
        select * from tbl_goods where g_id=#{
    
    xxx}
    </select>
	
   <insert id="addGoods">
       insert into tbl_goods values(default,#{
    
    g_name},#{
    
    g_price},#{
    
    g_date})
   </insert>

    <update id="updateGoods">
        update tbl_goods set g_name=#{
    
    g_name},g_price=#{
    
    g_price},g_date=#{
    
    g_date}
        where g_id=#{
    
    g_id}
    </update>

    <delete id="delGoods">
        delete from tbl_goods where g_id=#{
    
    g_id}
    </delete>
</mapper>

5.程序实现(在类中加载配置文件)

public class MyBatisTest{
    
    
    public static void main(String[] args) {
    
    
        delete();
    }
 private static void queryGoods() {
    
    
        //读取配置文件
        InputStream inputStream = MyBatisTest2.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        //解析配置文件并生成一个SqlSessionFactory对象(SqlSession工厂对象),该对象中使用工厂模式获得一个一个SqlSession对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //通过sqlSessionFactory对象获得SqlSession对象
        //sqlSession对象表示一次关于数据库的会话,其中包含连接对象及MyBatis对数据库操作进行封装的相关方法
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //通过sqlSession操作数据库
        //通过MyBatis动态代理机制获得一个GoodsDao的实现类对象(实现类是一个代理对象不是我们开发)
        GoodsDao goodsDao = sqlSession.getMapper(GoodsDao.class );
        List<Goods> goodsList = goodsDao.queryGoods();
        for (Goods goods : goodsList){
    
    
            System.out.println(goods);
        }
        //关闭SqlSession,释放相关资源
        sqlSession.close();
    }
}

三、MyBatis使用详解

3.1 使用Mapper代理对象进行操作:

使用SqlSession的常用方法如insert、update、delete、selectList、selectOne等方法可以完成数据库的常用操作,但操作不方便,不够直观,所以我们一般使用Mapper映射器的代理对象直接调用mapper映射器中的方法进行操作;
使用mapper代理对象前提条件:
(1)mapper.xml文件中的namespace必须为mapper接口的完整路径
(2)mapper接口中的方法名必须和mapper.xml中每个SQL操作标签的id一致

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.findUser();
3.2 理解SqlSessionFactory和SqlSession

​ (1) SqlSessionFactory是一个重量级组件,线程安全的,一般情况下一个数据库对应一个SqlSessionFactory对象,所以一般情况下一个应用仅需要一个SqlSessionFactory对象;通过SqlSessionFactory生成SqlSession对象;
//创建SqlSessionFactory对象时默认使用myBatis核心配置文件中的默认环境

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);

如果要使用其他的数据库环境则在创建工厂对象时需指定环境

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input,test);
(2) SqlSession是一个轻量级组件,非线程安全的,该对象通过SqlSessionFactory对象来获得,SqlSession对象我们可以看成一个数据库连接对象。该对象中封装了大量操作数据库的方法;由于SqlSession是非线程安全的,每次使用时要获得一个SqlSession对象,使用结束后关闭该对象;

SqlSessionFactory 有六个方法创建 SqlSession 实例。通常来说,当你选择其中一个方法时,你需要考虑以下几点:
 事务处理:你希望在 session 作用域中使用事务作用域,还是使用自动提交(auto-commit)?(对很多数据库和/或 JDBC 驱动来说,等同于关闭事务支持)
 数据库连接:你希望 MyBatis 帮你从已配置的数据源获取连接,还是使用自己提供的连接?
 语句执行:你希望 MyBatis 复用 PreparedStatement 和/或批量更新语句(包括插入语句和删除语句)吗?

		SqlSession openSession()//**默认的,非自动提交事务**
		SqlSession openSession(boolean autoCommit)//**自动提交事务**
		SqlSession openSession(Connection connection)//**使用自己提供的连接(不适用数据源提供的连接)**
		SqlSession openSession(ExecutorType execType)/**/指定处理器类型**

默认的 openSession() 方法没有参数,它会创建具备如下特性的 SqlSession:

 事务作用域将会开启(也就是不自动提交)。
 将由当前环境配置的 DataSource 实例中获取 Connection 对象。
 事务隔离级别将会使用驱动或数据源的默认设置。
 预处理语句不会被复用,也不会批量处理更新。

所以我们一般使用openSession(ExecutorType.BATCH)这个方法获取SqlSession对象
你可能对 ExecutorType 参数感到陌生。这个枚举类型定义了三个值:

 ExecutorType.SIMPLE:该类型的执行器没有特别的行为。它为每个语句的执行创建一个新的预处理语句。
 ExecutorType.REUSE:该类型的执行器会复用预处理语句。
 ExecutorType.BATCH:该类型的执行器会批量执行所有更新语句,如果 SELECT 在多个更新中间执行,将在必要时将多条更新语句分

隔开来,以方便理解。

使用SqlSession的常用方法如insertupdatedeleteselectListselectOne等方法可以完成数据库的常用操作,但操作不方便,不够直观,所以我们一般使用Mapper映射器的代理对象直接调用mapper映射器中的方法进行操作;

使用mapper代理对象前提条件:

(1) mapper.xml文件中的namespace必须为mapper接口的完整路径

(2) mapper接口中的方法名必须和mapper.xml中每个SQL操作标签的id一致

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

List<User> users = userMapper.findUser();
3.3 封装MyBatisUtils类

​ 为了保证SqlSessionFactory在整个应用中只有一个对象,也为了更方便的使用SqlSession对象,我们对SqlSessionFactory对象和SqlSession对象做简单的封装处理。

/***
 * 首先在MyBATIS中有三种executor:
 * SimpleExecutor -- SIMPLE 就是普通的执行器。
 * ReuseExecutor -执行器会重用预处理语句(prepared statements)
 * BatchExecutor --它是批量执行器
 */
public class MybatisUtils {
    
    
    //获得一个SqlSessionFactory对象           这里的参数是主配置文件
    private  static  final SqlSessionFactory FACTORY=creatSqlsessionFactory("Mybatis-config.xml");

    private static SqlSessionFactory creatSqlsessionFactory(String config) {
    
    
        InputStream inputStream = MybatisUtils.class.getClassLoader().getResourceAsStream(config);
        return  new SqlSessionFactoryBuilder().build(inputStream);
    }
    public   static SqlSession getSqlsession(){
    
    
        return FACTORY.openSession(ExecutorType.BATCH);
    }
}
//以后咱在使用就直接使用类名调getSqlsession方法生成 Sqlsession对象
3.4 映射器传参方式

​ (1) 如果映射器方法的参数为一个简单参数,占位符参数名可以任意

​ (2) 如果映射器方法的参数为多个时,占位符参数名按照方法定义参数的顺序依次为arg0,arg1,arg2,……或者param1,param2,param3……

​ (3) 可以通过@Param注解指定占位参数名

​ (4) 通过JavaBean传递参数,占位参数名为属性名

​ (5) #{}与${}的区别

​ #{}生成的是带有问号占位符的SQL语句

​ ${}生成的是使用连接字符串拼接而成的SQL语句

结果映射

​ MyBatis执行后可以返回的结果可以是基本类型、JavaBean对象、List集合、Map集合等,

结果映射是指将数据库中的列映射到Java对象的相对应的 属性中;

​ 在Mapper.xml中存在两个结果映射:1.resultType 2.resultMap

  1. resultType:可以指定基本类型,如int,double,string等也可以指定为自定义对象类型,但要求自定义对象中的属性和数据库查询的列名一致

    <?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">
    <!--
        SQL映射文件,在其中可以配置
            1.SQL映射
            2.对象与表中字段的映射
            3.对象与对象之间的关联映射
        namespace:指定当前映射的命名空间(是一个虚拟路径),只是为了实现MyBatis的动态代理功能
                    一般把namespace设置为Dao接口的路径
    -->
    <mapper namespace="xxx.xxx.com.jiazhong">
        <!--
            SQL映射
            select标签表示查询SQL语句
            id:当前SQL映射的唯一表示
            resultType:当前SQL语句查询结果中单条数据要封装的Java对象(使用实体类的完整路径名)
            parameterType:指定SQL映射中需要的参数(可以省略)
        -->
        <select id="queryGoods" resultType="com.jiazhong.mybatis.model.Goods">
            select * from tbl_goods
        </select>
    
        <!--
            当SQL语句中只需要一个参数时,参数名可以任意,通过#{name}占位符来表示
        -->
        <select id="queryGoodsById"  resultType="com.jiazhong.mybatis.model.Goods">
            select * from tbl_goods where g_id=#{xxx}
        </select>
    
        <!--
            当SQL映射中需要传入一个自定义对象时,SQL语句中的参数名不能任意,必须为对象的属性名(get方法去掉get字符串并将首字母小写的名字)
        -->
        <select id="queryGoodsByIdAndName" parameterType="com.jiazhong.mybatis.model.Goods" resultType="com.jiazhong.mybatis.model.Goods">
            select * from tbl_goods where g_id=#{g_id} and g_name like concat('%',#{g_name},'%')
        </select>
    </mapper>
    
  2. resultMap:属于高级映射,当Java对象中的属性名和数据库中的列名不一致时,就需要使用resultMap来将数据库中的列映射到Java对象中的属性

resultMap可以继承resultMap,当对象中的属性和表中的字段名一致时可以使用**autoMapping=“true”**实现自动映射

<mapper namespace="com.jiazhong.mybatis.dao.GoodsDao">
    <!--
        配置结果映射-将表中的字段与类中的属性进行匹配
        id:当前结果映射的标识符,唯一标识
        type:当前结果映射的类
    -->
    <resultMap id="goodsMap" type="com.jiazhong.mybatis.model.Goods">
        <!--主键映射
            column:指定SQL语句中的列名
            property:对象中属性名  -->
        
        <id column="id" property="g_id"></id>
        <!--普通列映射  -->
        <result column="name" property="g_name"></result>
        <result column="price" property="g_price"></result>
        <result column="date" property="g_date"></result>
    </resultMap>
    <!--
        SQL映射
        select标签表示查询SQL语句
        id:当前SQL映射的唯一表示
        resultType:当前SQL语句查询结果中单条数据要封装的java对象(使用实体类的完整路径名)
        parameterType:指定SQL映射中需要的参数(可以省略)
    -->
    <select id="queryGoods" resultType="goodsMap">
        select * from tbl_goods
    </select>
</mapper>

五、动态SQL

​ MyBatis 的强大特性之一便是它的动态SQL,通过MyBatis的动态SQL可以对SQL语句进行灵活拼接操作。

​ 在使用JDBC或其他持久化框架要拼接SQL是件很痛苦的事情,而使用MyBatis则简单的多,MyBatis提供了几个简单的标签来实现动态SQL的拼接;

数据库设计:

image-20210123150726821

if标签:使用if标签根据条件动态拼接SQL
需求:根据商品名称、价格区间、品牌进行综合查询,如果没有选择查询条件则查询全部商品

注意:如图上拼接会出现问题!!!!

image-20210123151031986

注意: 此方式拼接出来的字符串如法将第一个and去掉,如果条件都无法满足时sql语句后面多一个where,会造成SQL语法错误

解决方案:

Where标签:如果标签体中的内容存在自动在SQL语句中添加where关键字,如果where后面第一个关键字是and或or则自动去掉

image-20210123151313234

choose、when、otherwise标签:这三个标签搭配使用,类似java中的switch语句,当在多个条件中只需要其中一个则使用choose标签
价格区间最多只能选择一种,使用choose标签实现:

image-20210123151416679

Set标签:动态更新标签,如果需要根据内容动态更新时使用该标签,该标签会自动在SQL语句中加上set关键字并去掉多余的逗号
需求:根据需要更新表中的内容

image-20210123151523748

Trim标签:自定义标签,该标签可以实现where标签和set标签的功能,主要用于在前缀、后缀增加内容及删除前缀或后缀的内容

Prefix: 设置前缀追加的内容
prefixOverrides: 设置前缀要移除的内容
suffix: 设置后缀要追加的内容
suffixOverrides: 设置后缀要移除的内容

image-20210123151750508

foreach标签:遍历标签,
collection:指定要遍历的集合(数组:array,list:collection|list, set:collection|list,map: parameter)

​ (item:将集合中的元素赋值给item指定的变量
​ open:前缀添加内容
​ close:后缀添加内容
​ separator:每次循环分割内容)

需求:批量删除

image-20210123152022092


image-20210123152031507


**SQL片段:**为达到SQL语句复用的效果,MyBatis提供SQL片段,SQL片段定义好后可以被多次调用
定义SQL片段:

image-20210123152335388

image-20210123152352532


六、关联映射

关联映射指如何将多个有关联的表映射到对应的关联对象中

​ 对象之间的关联关系是有方向的可以为单向也可以是双向

数据库实例:

用户表

image-20210123152459111

商品表:

image-20210123152555151

订单明细表:

image-20210123152624642

用户表与商品表:多对多(多对多的关系都是由第三方间接关联)

用户表与订单表:一对多

订单表与用户表:多对一

订单表与明细表:一对多

明细表与订单表:多对一

一对多关联
需求:获得用户的订单,根据用户编号获得该用户及该用户所对应的订单信息
实体对象:在User对象中添加可以存储多个订单的List集合

image-20210123152800207

配置映射文件(通过关联对象查询方式实现):

image-20210123152914965

配置映射文件(通过表连接方式实现):

image-20210123153246797

多对一关联
需求:获得订单及所对应的用户信息(根据订单编号获得订单信息及订单所对应的用户信息)
订单表与用户表:
实体对象:增加关联属性

image-20210123153326862

映射文件:

image-20210123153410232

基于表连接:

image-20210123153439658

多对多关联:
需求:获得用户所购买的商品信息
用户—>订单—>订单明细—>商品

实体类:
用户实体加入订单集合
订单实体加入订单明细集合
订单明细实体加入商品对象

Mapper.java

image-20210123153645347

SQL语句:

image-20210123153717232

Mapper.xml
方式一,在一个xml中配置关联映射

image-20210123153811663

image-20210123153855605

image-20210123153909983


七、延迟加载(LazyLoading)

由于对象之间存在关联,在查询一个对象时MyBatis默认会将其关联对象也查询出来;这无疑增加了数据库的压力,为了降低数据库的压力及提高访问效率,MyBatis对关联映射提供延迟加载(懒加载)策略;
延迟加载是一种按需加载的方式执行数据库查询,根据需要来判断是否查询关联对象
MyBatis中延迟加载是使用多个查询语句实现的
MyBatis默认使用全局立即加载策略,需要我们收到配置某个查询的延迟加载策略,在关联映射中使用fetchType 属性可以配置关联对象的抓取策略(默认为立即加载关联对象, lazy延迟加载)
开启MyBatis的全局延迟加载策略:

image-20210123154141586

根据用户编号获得用户信息,根据需要查询用户订单信息
UserMapper.xml

image-20210123154253891

一般情况下,在一对多关联时,一方使用延迟加载的策略获取关联对象(多方)
在多对一的情况下,多方一般使用立即加载所对应的一方对象(关联对象)
注意:MyBatis中如果使用表连接来查询,则无法使用延迟加载机制


八、MyBatis缓存机制

在MyBatis中为提高查询效率,降低数据库负担,MyBatis提供缓存机制,通过缓存机制降低与数据库的交互次数

MyBatis的缓存有两种:
1.一级缓存(sqlSession级缓存):在同一个sqlSession中起作用,sqlSession一旦关闭,一级缓存消失;
2.二级缓存(mapper级缓存):在同一个mapper中多个sqlSession可以共享的缓存

一级缓存的使用:

​ 一级缓存属于mybatis的默认缓存,默认开启,直接使用
​ 当我们执行查询语句时,mybatis都会将查询的数据存在一级缓存中,直到session关闭或事务提交或手动清空缓存
当执行查询时,mybatis先在一级缓存中查找待查询的数据,如果一级缓存中存在则直接使用不在访问数据库,如果一级缓存中不存在则访问数据库查询,将查询的结果向一级缓存中存储一份,等待后续使用;
当执行commit事务提交时,会自动清空一级缓存中的数据;

二级缓存:

​ 二级缓存脱离sqlSession存在的,而且是同一个mapper共享的,所以默认情况对象并不进入二级缓存,需要手动设置要向二级缓存中存放的对象(一般情况下使用比较频繁的对象放入二级缓存)
​ 在Mapper中通过标签让当前对象存入到二级缓存中
​ 需要存入到二级缓存的对象,必须实现序列化接口
​ Mybatis中默认开启二级缓存,在需要的mapper(映射器)中配置要缓存的对象即可使用
​ 当查询一个对象时,会现在二级缓存中查找(该对象允许存入到二级缓存中),如果找到则直接返回,如果找不到则在到一级缓存中查找,如果找到则返回,如果招不到会将数据库发送查询请求,并查询相应的数据,将该数据存入到一级缓存和二级缓存(该对象允许存入到二级缓存中),以备后期使用


九、MyBatis注解

注:MyBatis的操作流程:
1.读取核心配置文件
2.创建SqlSessionFactory,是重量级组件,是线程安全的,一个sessionFactory对应一个数据库环境
3.创建SqlSession,轻量级,非线程安全的
4.使用SqlSession进行持久化操作

MyBatis常用注解:
1.@Insert:等同于<insert>定义insert SQL语句
2.@Update:等同于<updatet>定义update SQL语句
3.@Delete: 等同于<delete>定义delete SQL语句
4.@Select: 等同于<select>定义select SQL语句
5.@Results:等于与<resultMap>定义结果映射
6.@Result:等同于<id>和<result>,用于映射属性
6.@ResultMap:用于引用Results定义的结果映射
7.@one:等同于<association>,用于多对一关联中映射单个对象
8.@Many: 等同于<collection>,用于一对多关联中映射多方
需求:
1.根据用户编号获得用户信息及订单信息
2.根据用户编号获得用户信息及订单详情(含购买商品信息)

注解多表连接

查询所有用户:

public interface IUserDao {
    
    
    /**
     * 查询所有用户
     * @return
     */
    @Select("select * from user")
    @Results(id="userMap",value={
    
    
            @Result(id=true,column = "id",property = "userId"),
            @Result(column = "username",property = "userName"),
            @Result(column = "address",property = "userAddress"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "birthday",property = "userBirthday"),
            @Result(property = "accounts",column = "id",
                    many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",
                                fetchType = FetchType.LAZY))
    })
    List<User> findAll();
}

查询所有账户,并且获取每个账户所属的用户信息:

public interface IAccountDao {
    
    

    /**
     * 查询所有账户,并且获取每个账户所属的用户信息
     * @return
     */
    @Select("select * from account")
    @Results(id="accountMap",value = {
    
    
            @Result(id=true,column = "id",property = "id"),
            @Result(column = "uid",property = "uid"),
            @Result(column = "money",property = "money"),
            @Result(property = "user",column = "uid",one=@One(select="com.itheima.dao.IUserDao.findById",fetchType= FetchType.EAGER))
    })
    List<Account> findAll();

如需帮助 请下方留言

猜你喜欢

转载自blog.csdn.net/weixin_45894479/article/details/113054686
今日推荐