Java学习记录 MyBatis框架应用

本文详细介绍了MyBatis框架的初次应用、对象资源管理、SqlSessionFactory的构建,以及Mapper动态代理、参数映射、输出映射和全局配置等内容,涵盖了数据库操作和配置优化的关键知识点。
摘要由CSDN通过智能技术生成


参考文档: mybatis – MyBatis 3

Mybatis框架

MyBatis 是一个开源、轻量级的数据持久化框架,是 JDBC 和 Hibernate 的替代方案。MyBatis 内部封装了 JDBC,简化了加载驱动、创建连接、创建 statement 等繁杂过程,只需关注 SQL 语句即可

数据持久化是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中数据模型的统称。例如,文件的存储、数据的读取以及对数据表的增删改查等都是数据持久化操作

ORM 是一种数据持久化技术,它在对象模型和关系型数据库之间建立起对应关系,解决了实体类和数据库表映射的问题,并且提供了一种机制,通过 JavaBean 对象去操作数据库表中的数据。

Object: java对象

Relation: 关系,库中的表

Mapping: 映射

首次应用Mybatis

应用前提:

  • 引入 mybatis架构依赖 mysql-connector-java底层连接依赖 junit代码测试
  • 数据库表中的列名称 与 实体类属性的名称 相同

项目结构

  .
  |
  ├── src
  |    ├── main
  |	   |	├── java
  |	   |	|	  └── com
  |	   |    |   	   ├── dao
  |    |    |          |	├── TeamDao
  |    |    |          |    └── TeamDaoImpl
  |	   |	|	  	   ├── pojo
  |	   |	|	  	   |	├── Team
  |	   |	|		   |	└── Team.xml
  |	   |    | 		   └── utils		
  |	   |    |    	   		└── MybatisUtil
  |	   |	└──	resources
  |	   |		    ├── log4j.properties
  |	   |			└── mybatis.xml
  |	  test
  |    └── ...
  └── pom.xml

引入依赖 pom.xml

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.23</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

创建 Team实体类mybatis库team表数据

CREATE TABLE `team` (
	`teamId` int NOT NULL AUTO_INCREMENT COMMENT '球队ID',
	`teamName` varchar(50) DEFAULT NULL COMMENT '球队名称',
	`location` varchar(50) DEFAULT NULL COMMENT '球队位置',
	`createTime` date DEFAULT NULL COMMENT '球队建立时间',
	PRIMARY KEY (`teamId`)
) ENGINE=InnoDB AUTO_INCREMENT=1003 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
package com.pojo;

import java.util.Date;

public class Team {
    private Integer teamId;
    private String teamName;
    private String location;
    private Date createTme;
    
    /*省略setter和getter方法*/
    
    @Override
    public String toString() {
        return "Team{" +
                "teamId=" + teamId +
                ", teamName='" + teamName + '\'' +
                ", location='" + location + '\'' +
                ", createTme=" + createTme +
                '}';
    }
}

配置 连接文件 mybatis.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 环境-->
    <environments default="development">
        <!--id:数据源的名称-->
        <environment id="development">
            <!--事务类型:使用 JDBC 事务,使用 Connection 的提交和回滚-->
            <transactionManager type="JDBC"/>
            <!--数据源 dataSource:创建数据库 Connection 对象
            type: POOLED 使用数据库的连接池
            -->
            <dataSource type="POOLED">
                <!--连接数据库的四大参数
                注意 加载驱动是 MySQL8以上,否则 driver和url 都不一样,可参考学过的JDBC-->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?
useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;serverTimezone=GMT"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 注册映射文件 -->
    <mappers>
        <mapper resource="com/pojo/Team.xml"/>
    </mappers>
</configuration>

配置 映射文件 Team.xml(映射实体类与库中列的数据)

<?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">
<!--namespace="名称必须与映射的类的名字一致,是完全限定名"-->
<mapper namespace="com.pojo.Team">
    <!-- id="自定义名称,id不能重复;相当于dao中的方法名称"
    resultType="使用的要求:实体类中的属性名与表中的列名一致"
    -->
    <select id="findAll" resultType="com.pojo.Team">
        select * from team;
    </select>
</mapper>

配置 映射文件的扫描 pom.xml

<build>
    <resources>
        <resource>
            <!--所有目录-->
            <directory>src/main/java</directory>
            <includes>
                <!--包括目录 .properties, .xml 文件都会扫描到!!-->
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
    <plugins>
        ···
    </plugins>
</build>

测试查询

@Test
public void test() throws IOException {
    // 1. 读取 mybatis配置文件
    Reader reader = Resources.getResourceAsReader("mybatis.xml");
    // 2. 创建 SqlSessionFactoryd对象 , 目的 获取 sqlsession
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
    // 3. 创建可执行SQL语句的 SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 4. 执行 SQL语句
    List<Team> teamList = sqlSession.selectList("com.pojo.Team.findAll");
    // 5. 遍历结果
    System.out.println("遍历结果:");
    teamList.forEach(System.out::println);
    // 6. 释放资源
    sqlSession.close();
}

/* 执行结果

遍历结果:
Team{teamId=1, teamName='张三', location='上海', createTme=null}
Team{teamId=2, teamName='李四', location='深圳', createTme=null}
Team{teamId=3, teamName='王五', location='南京', createTme=null}
Team{teamId=4, teamName='赵六', location='广州', createTme=null}
Team{teamId=5, teamName='小七', location='南宁', createTme=null}

*/

Mybatis对象

Resources

org.apache.ibatis.io.Resources类

用于读取资源文件。有很多方法通过加载并解析资源文件,返回不同类型的 IO 流对象

SqlSessionFactoryBuilder

org.apache.ibatis.session.SqlSessionFactoryBuilder类

SqlSessionFactory 的创建,需要使用SqlSessionFactoryBuilder对 象的 build() 方法 。 事实上使用SqlSessionFactoryBuilder的原因是将SqlSessionFactory这个复杂对象的创建交由Builder来执行,也就是使用了建造者设计模式。

建造者模式(又称生成器模式):是一种对象的创建模式。可以将一个产品的 内部表象 与 产品的生成过程 分割开来,从而可以使一个建造过程生成具有 不同的内部表象的产品(将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示), 这样用户只需指定需要建造的类型就可以得到具体产品,而不需要了解具体的建造过程和细节。

在建造者模式中,角色分指导者(Director)与建造者(Builder): 用户联系指导者,指导者指挥建造者,最后得到产品,建造者模式可以强制实行 一种分步骤进行的建造过程

SqlSessionFactory

org.apache.ibatis.session.SqlSessionFactory接口

创建 SqlSession 需要使用 SqlSessionFactory 接口的的 openSession() 方法,使用前提需考虑一下几点:

  • 事务处理: 在session作用域中 ,是否使用自动提交?(autoCommit)
  • 数据库连接: 在 MyBatis中,是否使用自己提供的连接?(connection)
  • 语句执行: 在 MyBatis中,是否使用,是否 复用 PreparedStatement 通道进行 批处理 ?
public interface SqlSessionFactory {

  SqlSession openSession();

  SqlSession openSession(boolean autoCommit);

  SqlSession openSession(Connection connection);

  SqlSession openSession(TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType);

  SqlSession openSession(ExecutorType execType, boolean autoCommit);

  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType, Connection connection);
  //这个方法会返回一个 Configuration 实例,你可以在运行时使用它来检查 MyBatis 的配置
  Configuration getConfiguration();
}
方法参数名类型说明
autoCommitbooleantrue事务自动提交,否则关闭
connectionConnection应用自己提供的连接
execTypeExecutorType枚举定义定义了三个值
ExecutorType.SIMPLE:为每个语句的执行创建一个新的预处理语句
ExecutorType.REUSE:执行器会复用预处理语句
ExecutorType.BATCH:执行器会批量执行所有更新语句

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

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

SqlSessio

org.apache.ibatis.session.SqlSession接口

SqlSession 接口对象用于执行持久化操作。一个 SqlSession 对应着一次数据库会话,一次会话以SqlSession 对象的创建开始,以 SqlSession 对象的关闭结束

SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其 close()方法,将其关闭。再次需要会话,再次创建。 SqlSession 在方法内部创建,使用完毕后关闭

SqlSession 类中有超过 20 个方法,我们常用的几乎都是执行语法相关的方法

以下方法被用来执行定义在 SQL 映射 XML 文件中的 SELECT、INSERT、UPDATE 和 DELETE 语句。可通过名字快速了解它们的作用,每一方法都接受语句的 ID 以及参数对象,参数可以是原始类型(支持自动装箱或包装类)、JavaBean、POJO 或 Map。

<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
<T> Cursor<T> selectCursor(String statement, Object parameter)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)

说明:

  • insert、update、delete 方法返回值为影响的行数
  • selectOne:返回对象 或 null;selectList:返回多个对象 或 null
  • 游标(Cursor)与列表(List)返回的结果相同,不同的是,游标借助迭代器实现了数据的惰性加载
  • selectMap 方法 ,它会将返回的对象的其中一个属性作为 key 值,将对象作为 value 值

MyBatis构架

  1. Mybatis.xml文件是mybatis框架的全局配置文件,配置了mybatis框架运行的环境等信息。
  2. Mapperxx.xml是SQL的映射文件,文件中配置了所有的操作数据库的sql语句,这些文件需要在全局配置文件中加载
  3. 通过mybatis环境等配置信息构建SqlSessionFactroy ,相当于是产生连接池
  4. 由会话工厂创建SqlSession即会话(连接),操作数据库需要通过SqlSession进行的
  5. Mybatis底层自定义了Executor执行器的接口操作数据库,Executor接口有两个实现,一个基本的执行器,一个是缓存的执行器
  6. Mapped statement 也是mybatis框架一个底层的封装对象,包装了mybatis配置信息以及sql映射信息。Mapper.xml文件中的一个SQL语句对应一个Mapped statement对象,sql的id就是Mapped statement的id
  7. Mapped statement对SQL执行输入参数的定义,输入参数包括HashMap、基本类型、pojo,Executor通过Mapped statemen在执行SQL语句 前将输入java对象映射到sql语句中,执行完毕SQL之后,输出映射就是JDBC编码中的对preparedStatement 执行结果的定义

配置日志

实现步骤:

  1. 添加依赖 log4j
  2. 创建日志配置文件 log4j.properties
  3. mybatis.xml 配置文件添加日志配置

依赖

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

创建日志配置文件 log4j.properties

# Global logging configuration info warning error  选择日志呈现种类
log4j.rootLogger=DEBUG,stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

mybatis.xml 配置文件添加日志配置

<configuration>
<!--    日志配置-->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    ·····
</configuration>

测试 : 运行时会提示出运行明细的日志

Mapper动态代理

前面定义的 Dao接口和Dao实现类 没有实质性的工作意义,因此我们弃用Dao,可通过 SqlSession 的相关 API 定位到映射文件 mapper 中相应 id 的 SQL 语句,真正对 DB 进行操作的工作其实是由框架通过 mapper 中的 SQL 完成的,该形式被称为 Mapper接口 的动态代理方式

要点说明:

  • Mapper 动态代理方式无需实现 Dao 接口。接口是由 MyBatis 结合映射文件自动生成的动态代理实现的
  • Mapper(映射) 文件里 sql语句中的标签id值 必须对应 接口中的方法名 一致
  • resources配置文件夹,在idea开发工具不能跨级进行创建,必须手动逐级创建路径
  • Mapper(映射) 在resources配置文件夹中的路径必须 与 接口中的完全限定名 一致
  • SqlSession对象 需要通过 getMapper(Class<T> type)方法 获取代理对象,可获取指定接口的实现类对象
  • Mapper(映射) 文件里 mapper标签的namespace属性值 需要指定 接口的完全限定名
  • 每次添加映射 都需要去 mybatis.xml 的 mappers标签 进行添加注册映射文件
  • 进行 增删改 时需要 MybatisUtil工具类 获取SqlSession对象 进行提交

实例应用

项目结构

  .
  |
  ├── src
  |    ├── main
  |	   |	├── java
  |	   |	|	  └── com
  |	   |    |   	   ├── mapper
  |    |    |          |    └── TeamMapper
  |	   |	|	  	   ├── pojo
  |	   |	|		   |	└── Team
  |	   |    | 		   └── utils		
  |	   |    |    	   		└── MybatisUtil
  |	   |	└──	resources
  |	   |		    ├── log4j.properties
  |	   |			├── mybatis.xml
  |    |			└── com
  |    |				 ├── mapper
  |    |				 |	   └── TeamMapper.xml
  |    |				 └── pojo
  |    |				  	   └── Team.xml
  |	  test	
  |    └── ...
  └── pom.xml

创建 TeamMapper接口

package com.mapper;

import com.pojo.Team;

import java.util.List;

public interface TeamMapper {
    List<Team> findAll();
    Team queryById(int id);
    Team queryByName(String name);
    int add(Team team);
    int update(Team newTeam);
    int delById(int id);
    int delByName(String name);
}

创建 TeamMapper.xml映射配置文件

<?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">
<!--namespace="名称必须与映射的类的名字一致,是完全限定名"-->
<mapper namespace="com.mapper.TeamMapper">

    <!--
    id="自定义名称,id不能重复;相当于dao中的方法名称"
    resultType="使用的要求:实体类中的属性名与表中的列名一致"
    -->
<!--    查询所有-->
    <select id="findAll" resultType="com.pojo.Team">
        select * from team;
    </select>

<!--    查询指定-->
    <select id="queryById" resultType="com.pojo.Team">
        select * from team where teamId=#{teamId}
    </select>
    <select id="queryByName" resultType="com.pojo.Team">
        select * from team where teamName=#{teamName}
    </select>

    <!--
    parameterType="指定对象作为参数"
    #{对象属性名}
    -->
<!--    添加数据-->
    <insert id="add" parameterType="com.pojo.Team">
        INSERT INTO `mybatis`.`team`(`teamName`, `location`, `createTime`) VALUES (#{teamName}, #{location}, #{createTime})
    </insert>

<!--    修改数据-->
    <update id="update"  parameterType="com.pojo.Team">
        UPDATE `team` SET teamName=#{teamName},location=#{location} WHERE teamId=#{teamId}
    </update>

<!--    删除数据-->
    <delete id="delById" parameterType="com.pojo.Team">
        DELETE FROM `mybatis`.`team` WHERE `teamId` = #{id}
    </delete>
    <delete id="delByName" parameterType="com.pojo.Team">
        DELETE  FROM `mybatis`.`team` WHERE `teamName` = #{name}
    </delete>

</mapper>

mybatis.xml配置文件 中注册映射文件

<configuration> 
    ·····
	<!-- 注册映射文件 -->
    <mappers>
		·····
        <mapper resource="com/mapper/TeamMapper.xml"/>
    </mappers>
</configuration>

测试:

import com.mapper.TeamMapper;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.junit.Test;

import java.util.Date;
import java.util.List;

public class TeamMapperTest {
    
    //前提 接口的方法名 与 映射sql 标签的id值 相同!!!
    private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
    
    @Test
    public void findAll(){
        List<Team> all = teamMapper.findAll();
        all.forEach(System.out::println);
    }
    
    @Test
    public void queryById(){
        Team team = teamMapper.queryById(2);
        System.out.println("team : " + team);
    }
    
    @Test
    public void queryByName(){
        Team team = teamMapper.queryByName("火箭");
        System.out.println("team : " + team);
    }
    
    @Test
    public void add(){
        Team team = new Team("公牛","洛杉矶",new Date());
        int add = teamMapper.add(team);
        MybatisUtil.getSqlSession().commit();
        System.out.println("add : " + add);
    }
    
    @Test
    public void update() {
        Team team = teamMapper.queryById(1009);
        team.setTeamName("老鸽");
        team.setLocation("南京");
        int update = teamMapper.update(team);
        MybatisUtil.getSqlSession().commit();
        System.out.println("update : " + update);
    }
    
    @Test
    public void delById() {
        int i = teamMapper.delById(1009);
        MybatisUtil.getSqlSession().commit();
        System.out.println("i : " + i);
    }
    
    @Test
    public void delByName() {
        int i = teamMapper.delByName("公牛");
        MybatisUtil.getSqlSession().commit();
        System.out.println("i : " + i);
    }
    
}

输入映射

传递多个参数

parameterType

parameterType 指定值是接口中方法参数的类型,类型必须是完全限定名 或 别名。该属性非必须,因为Mybatis框架能自行判断具 体传入语句的参数

Mybatis 提供以下 3 种方式,实现给映射器传递多个参数:

  • 方法直接传递参数
  • 使用注解传递参数
  • 使用 Map传递参数

应用说明:

  • 方法直接传递参数 进行传递 不同mybatis版本传递参数 在sql应用不一样
    • mybatis3.3之前:使用 #{0}、#{1}、…
    • mybatis3.3之后:使用 #{arg0},#{arg1}、… 或者 #{param1}、#{param2}、…
  • sql语句中 的 < 号 不能使用 ,需要 转义符 < : &lt; ; > :&gt;
  • 使用@Param注解后,只能应用 自定的注解名称

多参数应用

修改 TeamMapper接口 添加sql执行的方法

package com.mapper;

import com.pojo.Team;
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Map;

public interface TeamMapper {
    ····
    
    List<Team> queryByRange1(int min , int max);
    List<Team> queryByRange2(int min , int max);
    List<Team> queryByRange3(@Param("min") int min ,@Param("max") int max);
    List<Team> queryByRange4(Map<String,Object> map);
}

修改 TeamMapper.xml映射文件 添加sql语句

<!--arg 应用-->
<select id="queryByRange1" resultType="com.pojo.Team">
    select * from team where teamId>=#{arg0} and teamId &lt;= ${arg1}
</select>
<!--param 应用-->
<select id="queryByRange2" resultType="com.pojo.Team">
    select * from team where teamId>=#{param1} and teamId &lt;= ${param2}
</select>
<!--注解别名 应用-->
<select id="queryByRange3" resultType="com.pojo.Team">
    select * from team where teamId>=#{min} and teamId &lt;= ${max}
</select>
<!--Map 应用-->
<!--    传参 #{} Map集合中的 Key保持一致-->
<select id="queryByRange4" resultType="com.pojo.Team">
    select * from team where teamId>=#{min} and teamId &lt;= ${max}
</select>

测试:

import com.mapper.TeamMapper;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

//多参数传递应用
public class multiParameterTest {
    
    private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
    
    //arg 应用
    @Test
    public void test01() {
        List<Team> teamList = teamMapper.queryByRange1(1 , 6);
        teamList.forEach(team -> System.out.println(team));
    }
    
    //param 应用
    @Test
    public void tset02() {
        List<Team> teamList = teamMapper.queryByRange2(1 , 6);
        teamList.forEach(team -> System.out.println(team));
    }
    
    //接口参数名 应用 (需在接口方法中的参数定义名)
    @Test
    public void tset03() {
        List<Team> teamList = teamMapper.queryByRange3(1 , 6);
        teamList.forEach(team -> System.out.println(team));
    }
    
    //Map传递 应用
    @Test
    public void tset04() {
        Map<String,Object> map  = new HashMap<>();
        map.put("min",1);
        map.put("max",6);
        List<Team> teamList = teamMapper.queryByRange4(map);
        teamList.forEach(team -> System.out.println(team));
    }

}

#{} 和 ${} 区别

#{}:表示一个占位符,通知Mybatis 使用实际的参数值代替

${}:表示字符串原样替换,通知Mybatis 使用 $包含的“字符串”替换所在位置

示例:

配置接口方法

public interface TeamMapper {
 	···
    Team queryByType(@Param("type") String type,@Param("data") Object data);
}

映射文件

<select id="queryByType" resultType="com.pojo.Team">
    select * from team where ${type}=#{data}
</select>

测试(前提库有相应信息)

@Test
public void queryByType() {
    //teamId、teamName
    Team team1 = teamMapper.queryByType("teamId",1017);
    Team team2 = teamMapper.queryByType("teamName","Sanscan12");
    System.out.println("team1 : " + team1);
    System.out.println("team2 : " + team2);
}

输出映射

resultType

resultType:执行 sql 得到 ResultSet 转换的类型,使用类型的完全限定名 或 别名。如果返回的是集合,设置的是集合元素的类型,而不是集合本身。resultType 和 resultMap, 不能同时使用。

可通过以下方式进行映射输出:

  • 输出java基本属性类型
  • 输出Map类型
  • 输出pojo类型
  • 输出自定义resultMap类型

应用说明:

  • resultMap 专门用于数据库中的列和实体类不匹配的情况下使用
  • resultMap 是 自己编写表中的列名 与 实体类中的属性 的映射(他们不匹配的前提下需要自行匹配映射)

应用

修改 TeamMapper接口 添加sql执行的方法

package com.mapper;

import com.pojo.Team;
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Map;

public interface TeamMapper {
    ····
    
    int queryTotal();
    Map<String,Object> queryMap();
    List<Map<String, Object>> queryMapList();
    List<Team> queryAll2();
}

修改 TeamMapper.xml映射文件 添加sql语句

<!--    查单列单条数据 基本类型输出-->
    <select id="queryTotal" resultType="java.lang.Integer">
        select count(teamId) from team
    </select>
<!--    查多列单条数据 Map类型输出-->
    <select id="queryMap" resultType="java.util.HashMap">
        select min(teamId) 'min',max(teamId) 'max' from team
    </select>
<!--    查多列多条数据 List<Map>类型输出-->
    <select id="queryMapList" resultType="java.util.HashMap">
        select teamName,location from team
    </select>
<!--
	resultMap是 自己编写表中的列名 与 实体类中的属性 的映射(他们不匹配的前提下需要自行匹配映射)
		id: resultMap的名称,要求唯一
		type: 期待要映射为java的类型
	id 主键列 ; result 其余列
		column: 数据库中的列名,不区分大小写
		property: 实体类中的属性名,区分大小写
		javaType: 实体类中对应的属性类型
		jdbcType: 数据库中column类型(一般忽略)
-->
<!--    resultMap数据类型 自定义映射输出-->
    <select id="queryAll2" resultMap="baseResultMap">
        select * from team
    </select>
    <resultMap id="baseResultMap" type="com.pojo.Team">
        <id column="teamId" property="teamId" javaType="java.lang.Integer"/>
        <result column="teamName" property="teamName" javaType="java.lang.String"/>
        <result column="location" property="location" javaType="java.lang.String"/>
<!--        <result column="createTime" property="createTime" javaType="java.util.Date"/>-->
    </resultMap>

测试

import com.mapper.TeamMapper;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

//多参数传递应用
public class multiParameterTest {
    
    private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
    
    //映射输出形式
    
    //查单列单条数据
    @Test
    public void queryTotal() {
        int i = teamMapper.queryTotal();
        System.out.println("i : " + i);
    }
    
    //查多列单条数据
    @Test
    public void queryMap() {
        Map<String, Object> stringObjectMap = teamMapper.queryMap();
        System.out.println(stringObjectMap);
    }
    
    //查多列多条数据
    @Test
    public void queryMapList() {
        List<Map<String, Object>> mapList = teamMapper.queryMapList();
        for (Map<String, Object> stringObjectMap : mapList) {
            System.out.println(stringObjectMap);
        }
    }
    
    //处理自定义类型数据
    @Test
    public void queryAll2() {
        List<Team> teamList = teamMapper.queryAll2();
        teamList.forEach(team -> System.out.println(team));
    }
    
}

MyBatis全局配置文件

之前应用的 全局配置文件 mybatis.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(配置)
    • properties(属性)
    • settings(设置)
    • typeAliases(类型别名)
    • typeHandlers(类型处理器)
    • objectFactory(对象工厂)
    • plugins(插件)
    • environments(环境配置)
      • environment(环境变量)
        • transactionManager(事务管理器)
        • dataSource(数据源)
    • databaseIdProvider(数据库厂商标识)
    • mappers(映射器)

properties

properties标签 可在外部进行配置,并可以进行动态替换。properties标签 在 configuration标签里的 下一节点

  • properties子元素配置
  • 外部属性文件配置

以连接 数据库 的四个参数数据为例子

properties子元素配置

<?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>
<!--    引入配置文件-->
    <properties>
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;serverTimezone=GMT"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </properties>
	····
            <dataSource type="POOLED">
                <!--连接数据库的四大参数
                注意数据库版本使用的是 MySQL8以上,如果是mysql5的话,driver和url都不一样,参考学过的JDBC-->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
	····
</configuration>

外部属性文件配置 jdbc.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT
jdbc.username=root
jdbc.password=root
<?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>
<!--    引入配置文件-->
    <properties resource="jdbc.properties"/>
	····
            <dataSource type="POOLED">
                <!--连接数据库的四大参数
                注意数据库版本使用的是 MySQL8以上,如果是mysql5的话,driver和url都不一样,参考学过的JDBC-->
                <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>
	····
</configuration>

settings

MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。例如:日志、等…(以上实例有应用就不赘述了)

<!--配置日志-->
<settings>
	<setting name="logImpl" value="LOG4J"/>
</settings

typeAliases

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。typeAliases标签 在 settings标签 的下一节点

<?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>
	···
<!--    日志配置-->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
<!--    自定义别名-->
    <typeAliases>
        <!--对单个实体类-->
        <typeAlias type="com.pojo.Team" alias="Team"/>
        <!--批量定义别名:类似于扫描(首字母支持大小写)-->
        <package name="com.pojo"/>
    </typeAliases>
    ···
</configuration>

在应用于 映射文件 里 的 parameterTyperesultType 、…等属性

<!--查询所有-->
<select id="findAll" resultType="Team">
    select * from team;
</select>
<!--添加-->
<insert id="add" parameterType="Team">
    INSERT INTO `mybatis`.`team`(`teamName`, `location`, `createTime`) VALUES (#{teamName}, #{location}, #{createTime})
</insert>
<!--查询所有2-->
<select id="queryAll3" resultType="Team2">
    select teamId 'team_id',teamName 'team_name',location,createTime from team
</select>
<!--查单列单条数据-->
<select id="queryTotal" resultType="int">
    select count(teamId) from team
</select>
<!--查多列单条数据-->
<select id="queryMap" resultType="map">
    select min(teamId) 'min',max(teamId) 'max' from team
</select>

其他别名

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

Mappers

MyBatis 执行 SQL映射语句 前提需要告诉 MyBatis 映射文件路径在哪里,从而实现执行SQL语句

配置形式:

  • 使用 相对于类路径的资源 引用
  • 使用 映射器接口实现类 的完全限定类名
  • 使用 包类的映射器接口实现全部注册为映射器(推荐)
<!-- 注册映射文件 -->
<mappers>
    <!-- 
		相对于类路径的资源 引用 
		使用相对于类路径的资源,从 classpath 路径查找文件
	-->
    <mapper resource="com/mapper/TeamMapper.xml"/>
    <!-- 
		使用的mapper接口的完全限定名 
		要求:接口和映射文件同包同名
	-->
    <mapper class="com.mapper.GameRecordMapper"/>
    <!-- 
		指定包下的 所有Mapper接口 
		注意:此种方法要求 Mapper接口名称和 mapper 映射文件名称相同,且在同一个目录中。
	-->
    <package name="com.mapper"/>
</mappers>

MyBatis映射关系

在MySQL中,当两表之间存在着主从关系,那么从表有个外键 对应 主表的主键 !

以下是可能出现映射关系的情况:

  • 对一 映射关系
  • 对多 映射关系

对一 映射关系

对一映射方式有3种:

  • 通过关联对象打点调用属性的方式
  • 直接引用关联对象的Mapper映射
  • 直接引用关联对象的单独查询的方法

实现应用

前提:

  • 库中的player表要有数据(注意主从关系)
  • 全局配置文件 已注册映射文件

创建新表 mybatis库 player表

CREATE TABLE `player` (
  `playerId` int NOT NULL,
  `playerName` varchar(100) DEFAULT NULL,
  `playerNum` int DEFAULT NULL,
  `teamId` int DEFAULT NULL,
  PRIMARY KEY (`playerId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

创建 com.pojo.Player实体对象

package com.pojo;

public class Player {
    private int playerId;
    private String playerName;
    private int playerNum;
    private int teamId;
    
    //多对一的体现 ; 关联对象 多球员 -- 球队
    private Team team1;
    private Team team3;
    private Team team2;
    
    //省略 get 和 set 方法
    
    @Override
    public String toString() {
        return "Player{" +
                "playerId=" + playerId +
                ", playerName='" + playerName + '\'' +
                ", playerNum=" + playerNum +
                ", teamId=" + teamId +
                ", team1=" + team1 +
                ", team3=" + team3 +
                ", team2=" + team2 +
                '}';
    }
}

创建 com.mapper.PlayerMapper接口

package com.mapper;

import com.pojo.Player;
import org.apache.ibatis.annotations.Param;

public interface PlayerMapper {
    
    Player queryById1(@Param("id") int playerId);
    Player queryById2(int playerId);
    Player queryById3(int playerId);
    Player queryById4(int playerId);
    
}

创建 PlayerMapper.xml映射文件

<?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">
<!--namespace="名称必须与映射的类的名字一致,是完全限定名"-->
<mapper namespace="com.mapper.PlayerMapper">

    <select id="queryById1" resultType="Player">
         SELECT * FROM player WHERE playerId=#{id}
    </select>

    <select id="queryById2" resultMap="JoinTeamResult1">
        SELECT * FROM player p INNER JOIN team t
        WHERE p.teamId=t.teamId AND p.playerId=4
    </select>

    <select id="queryById3" resultMap="JoinTeamResult2">
        SELECT * FROM `player` p INNER JOIN team t
        WHERE p.teamId=t.teamId AND p.playerId=4
    </select>

    <select id="queryById4" resultMap="JoinTeamResult3">
        SELECT * FROM player WHERE playerId=#{id}
    </select>

    <resultMap id="baseResultMap" type="com.pojo.Player">
        <id column="playerId" property="playerId"/>
        <result column="playerName" property="playerName"/>
        <result column="playerNum" property="playerNum"/>
        <result column="teamId" property="teamId"/>
    </resultMap>

<!--    No.1
    通过关联对象打点调用属性的方式
    要求:连接查询
    如果连接查询,一般单独定义resultMap
    extends属性值为:继承其他 resultMap的id
    前提 team.teamId 与 player.teamId 相同
-->
    <resultMap id="JoinTeamResult1" type="player" extends="baseResultMap">
        <id column="teamId" property="team1.teamId"/>
        <result column="teamName" property="team1.teamName"/>
        <result column="location" property="team1.location"/>
        <result column="createTime" property="team1.createTime"/>
    </resultMap>

<!--    No.2
    直接饮用关联对象的Mapper映射
    要求:连接查询、关联对象中已经存在被引用的resultMap
        property:关联对象的属性名
        javaType:关联对象的类型
        resultMap:关联对象的命名空间中的resultMap
-->
    <resultMap id="JoinTeamResult2" type="Player" extends="baseResultMap">
        <association property="team2" javaType="Team" resultMap="com.mapper.TeamMapper.baseResultMap"/>
    </resultMap>

<!--    No.3
    直接引用关联对象的单独查询的方法
    要求:关联对象的Mapper中必须要求有独立的查询方法 如以下:TeamMapper中的queryById方法
        property:关联对象的属性名
        javaType:关联对象的类型
        select:关联对象的单独查询的语句
        column:外键列 => 查询从表的主键
-->
    <resultMap id="JoinTeamResult3" type="Player" extends="baseResultMap">
        <association property="team3" javaType="Team" select="com.mapper.TeamMapper.queryById" column="teamId"/>
    </resultMap>


</mapper>

测试

import com.mapper.PlayerMapper;
import com.pojo.Player;
import com.utils.MybatisUtil;
import org.junit.Test;

public class PlayerMapperTest {
    
    private PlayerMapper playerMapper = MybatisUtil.getSqlSession().getMapper(PlayerMapper.class);
    
    @Test
    public void queryById1() {
        Player player = playerMapper.queryById1(5);
        System.out.println("player : " + player);
    }
    
    @Test
    public void queryById2() {
        Player player = playerMapper.queryById2(2);
        System.out.println("player : " + player);
    }
    
    @Test
    public void queryById3() {
        Player player = playerMapper.queryById3(5);
        System.out.println("player : " + player);
    }
    
    @Test
    public void queryById4() {
        Player player = playerMapper.queryById4(3);
        System.out.println("player : " + player);
    }
    
}

对多映射关系

对多映射方式有2种:

  • 连接查询
  • 引用关联对象的单独查询的方法

实现应用

修改 Team类 添加对多的属性

public class Team {
    private Integer teamId;
    private String teamName;
    private String location;
    private Date createTime;
    
    //一对多的体现:一方持有多方的对象
    private List<Player> playerList1;
    private List<Player> playerList2;
    
    //省略 set 、get、toString 方法
}

修改 TeamMapper接口 添加应用方法

package com.mapper;

import com.pojo.Team;
import com.pojo.Team2;
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Map;

public interface TeamMapper {
	
    ····
    
    Team queryById1(int temaId);
    Team queryById2(int temaId);
}

修改 PlayerMapper接口 添加应用方法(用于实现方法2)

package com.mapper;

import com.pojo.Player;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author Sans
 */
public interface PlayerMapper {

    ····
    
    List<Player> queryByteamId(int teamId);
}

修改 PlayerMapper.xml

<?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">
<!--namespace="名称必须与映射的类的名字一致,是完全限定名"-->
<mapper namespace="com.mapper.PlayerMapper">

    ·····

<!--    用于对多映射的应用 -->
    <select id="queryByTeamId" resultType="Player">
        SELECT * FROM player WHERE teamId=#{id}
    </select>
</mapper>

修改 TeamMapper.xml

<?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">
<!--namespace="名称必须与映射的类的名字一致,是完全限定名"-->
<mapper namespace="com.mapper.TeamMapper">

   	 ·····
    
    <select id="queryById1" resultMap="joinResult1">
        SELECT * FROM team t INNER JOIN player p
        WHERE t.teamId = p.teamId AND t.teamId = #{id}
    </select>

    <select id="queryById2" resultMap="joinResult2">
        SELECT * FROM team WHERE teamId=#{id}
    </select>

<!--    No.1
    对多连接查询:对多使用 collection标签
        property:关联对象集合名称
        javaType:关联对象集合类型
        ofType:关联对象集合泛型
        resultMap:引用关联对象的结果映射
-->
    <resultMap id="joinResult1" type="Team" extends="baseResultMap">
        <collection property="playerList1" javaType="java.util.ArrayList"
                    ofType="player" resultMap="com.mapper.PlayerMapper.baseResultMap"/>
    </resultMap>

<!--    No.2
    对多连接查询:对多使用 collection标签
        property:关联对象集合名称
        javaType:关联对象集合类型
        select:引用关联对象的单独查询的方法(前提方法可用)
        column:引用关联对象的单独查询的方法的参数 , 一般是外键名!!!
-->
    <resultMap id="joinResult2" type="Team" extends="baseResultMap">
        <collection property="playerList2" javaType="java.util.ArrayList"
                    select="com.mapper.PlayerMapper.queryByTeamId" column="teamId"/>
    </resultMap>

</mapper>

测试

//对多映射测试
private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);

@Test
public void queryById1() {
    Team team = teamMapper.queryById1(1019);
    System.out.println("team : " + team);
    team.getPlayerList1().forEach(player -> System.out.println("\t"+player));
}

@Test
public void queryById2() {
    Team team = teamMapper.queryById2(1019);
    System.out.println("team : " + team);
    team.getPlayerList2().forEach(player -> System.out.println("\t"+player));
}

动态SQL

动态 SQL 是 MyBatis 的强大特性之一。在 JDBC 或其它类似的框架中,需要手动拼接 SQL 语句,避免了 在不同条件下拼接 SQL 语句的困难

动态SQL应用到的元素

标签节点作用
if判断语句
choose 、when、otherwise多条件分支判断语句
foreach循环语句
where、set辅助应用(处理SQL瓶装问题)

应用

应用前提:

  • 有库信息
  • 映射文件已注册(扫描包)

封装查询条件类 QueryTeamVO

package com.pojo;

import java.util.Date;

public class QueryTeamVO {
    
    /** 封装球队查询信息
     * 球队名 & 开始时间节点 & 结束时间节点 & 地点
     */
    private String name;
    private Date beginTime;
    private Date endTime;
    private String location;
    
   // 省略 set 、 get 方法 
    
    @Override
    public String toString() {
        return "QueryTeamVO{" +
                ", name='" + name + '\'' +
                ", beginTime=" + beginTime +
                ", endTime=" + endTime +
                ", location='" + location + '\'' +
                '}';
    }
}

添加应用方法 TeamMapper接口

package com.mapper;

import com.pojo.QueryTeamVO;
import com.pojo.Team;
import com.pojo.Team2;
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Map;

public interface TeamMapper {
    
    ····
    
    List<Team> queryByVO(QueryTeamVO vo);
    List<Team> queryByVO2(QueryTeamVO vo);
    
    int update2(Team team);
    
    int addList(List list);
    int delList(List list);
}

配置映射文件 TeamMapper.xml

<?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">
<!--namespace="名称必须与映射的类的名字一致,是完全限定名"-->
<mapper namespace="com.mapper.TeamMapper">

    ·····
    
    <resultMap id="baseResultMap" type="com.pojo.Team">
        <id column="teamId" property="teamId" javaType="java.lang.Integer"/>
        <result column="teamName" property="teamName" javaType="java.lang.String"/>
        <result column="location" property="location" javaType="java.lang.String"/>
        <result column="createTime" property="createTime" javaType="java.util.Date"/>
    </resultMap>

    ····
    
<!--    实现多条件查询if
        SQL中的 CONCAT函数说明:
            CONCAT("aa","bb") => aabb 拼接字符串
-->
    <select id="queryByVO" resultMap="baseResultMap">
        SELECT * FROM team
        <where>
            <if test="name!=null">
                AND teamName LIKE CONCAT(CONCAT('%',#{name}),'%')
            </if>
            <if test="beginTime!=null">
                AND createTime >= #{beginTime}
            </if>
            <if test="endTime!=null">
                AND createTime &lt;= #{endTime}
            </if>
            <if test="location!=null">
                AND location = #{location}
            </if>
        </where>
    </select>

<!--    choose、when、otherwise 标签应用
        标签说明:
            choose:指定分支
            when:条件分支节点
            otherwise:都未满足条件
-->
    <select id="queryByVO2" resultMap="baseResultMap">
        SELECT * FROM team WHERE 1=1
        <choose>
            <when test="name!=null">
                AND teamName LIKE CONCAT(CONCAT('%',#{name}),'%')
            </when>
            <when test="location!=null">
                AND location LIKE CONCAT(CONCAT('%',#{location}),'%')
            </when>
            <otherwise>
                AND 1=0
            </otherwise>
        </choose>
    </select>

<!--     set 标签应用-->
    <update id="update2">
        UPDATE team
        <set>
            <if test="teamName!=null"> teamName=#{teamName} </if>
            <if test="location!=null"> location=#{location} </if>
        </set>
        WHERE teamId=#{teamId}
    </update>

<!--    forEach 标签应用
        标签属性说明:
            collection:指定集合
            item:集合中的每个迭代对象
            separator:每次迭代之间的分隔符
            open:循环语句 以指定字符为 开头
            close:循环语句 以指定字符为 结束
-->
<!--    添加-->
    <insert id="addList">
        INSERT INTO `mybatis`.`team`(`teamName`, `location`, `createTime`) VALUES
        <foreach collection="list" item="team" separator=",">
            (#{team.teamName}, #{team.location}, #{team.createTime})
        </foreach>
    </insert>
<!--    删除-->
    <delete id="delList">
        DELETE FROM team WHERE teamId in
        <foreach collection="list" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </delete>
</mapper>

测试

import com.mapper.TeamMapper;
import com.pojo.QueryTeamVO;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

//动态SQL测试
public class SQLTest {
    
    private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
    
    //if标签节点应用
    @Test
    public void test01() {
        QueryTeamVO vo = new QueryTeamVO();
        // vo.setName("哥斯拉");
        // vo.setLocation("洛杉矶");
        vo.setEndTime(new Date());
        List<Team> teamList = teamMapper.queryByVO(vo);
        teamList.forEach(team -> System.out.println(team));
    }
    
    //choose、when、otherwise 标签应用
    @Test
    public void test02() {
        QueryTeamVO vo = new QueryTeamVO();
        //查 指定name的关键字
        vo.setName("哥");
        //查 指定location的关键字
        // vo.setLocation("洛");
        List<Team> teamList = teamMapper.queryByVO2(vo);
        teamList.forEach(team -> System.out.println(team));
    }
    
    //set 标签应用
    @Test
    public void test03() {
        Team team = new Team();
        team.setTeamId(1017);
        team.setTeamName("老鸽队");
        int num = teamMapper.update2(team);
        MybatisUtil.getSqlSession().commit();
        System.out.println("num : " + num);
    }
    
    //forEach 标签应用
    //添加
    @Test
    public void test04() {
        List<Team> list = new ArrayList<>();
        for (int i = 1 ; i <= 3 ; i++) {
            Team team = new Team();
            team.setTeamName("forTest"+i);
            team.setLocation("jd"+i);
            team.setCreateTime(new Date());
            list.add(team);
        }
        int i = teamMapper.addList(list);
        MybatisUtil.getSqlSession().commit();
        System.out.println("i : " + i);
    }
    
    //删除
    @Test
    public void test05() {
        List<Integer> list = new ArrayList<>();
        list.add(1022);
        list.add(1023);
        list.add(1024);
        list.add(1025);
        int i = teamMapper.delList(list);
        MybatisUtil.getSqlSession().commit();
        System.out.println("i : " + i);
    }
        
}

MyBatis缓存

MyBatis缓存是一般的ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力。

将经常查询的数据存在缓存(内存)中,用户查询该数据的时候不需要从库上 查询,而是直接从缓存中查询,提高查询效率,解决高并发问题。 MyBatis 也有一级缓存 和 二级缓存,并且预留了集成 第三方缓存的接口

一级缓存

Mybatis自带的 缓存 ,在构造sqlSession对象时内部有个 HashMap 结构存储缓存数据,它的作用域范围是 sqlSession 。如果两次查询用同一个sqlSession进行查询语句,则第一次会通过数据库获取到数据库 ,二次会缓存中获取数据!

缓存清除条件:

  • session.clearCache()
  • session.close()
  • 执行 增删改
  • 事务回滚
  • 事务提交

应用实例

import com.pojo.Team;
import com.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

public class CacheTest {
    
    private SqlSession sqlSession = MybatisUtil.getSqlSession();
    
    @Test
    public void test01() {
        Team t1 = sqlSession.selectOne("com.mapper.TeamMapper.queryById",1003);
        System.out.println("t1 : " + t1);
        Team t2 = sqlSession.selectOne("com.mapper.TeamMapper.queryById",1003);
        System.out.println("t2 : " + t2);
        
        MybatisUtil.close();
        
        //换 sqlSession ,刷新缓存
        sqlSession = MybatisUtil.getSqlSession();
        Team t3 = sqlSession.selectOne("com.mapper.TeamMapper.queryById",1003);
        System.out.println("t3 : " + t3);
        
        int num = sqlSession.delete("com.mapper.TeamMapper.delById",1000);
        sqlSession.commit();
        System.out.println("num : " + num);
        
        Team t4 = sqlSession.selectOne("com.mapper.TeamMapper.queryById",1003);
        sqlSession.close();
    }
    
}

/* 运行结果

DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - ==>  Preparing: select * from team where teamId=?
DEBUG [main] - ==> Parameters: 1003(Integer)
DEBUG [main] - <==      Total: 1
t1 : Team{teamId=1003, teamName='老八', location='温州', createTime=Wed Feb 17 00:00:00 CST 2021}
t2 : Team{teamId=1003, teamName='老八', location='温州', createTime=Wed Feb 17 00:00:00 CST 2021}
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - Returned connection 527829831 to pool.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 527829831 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - ==>  Preparing: select * from team where teamId=?
DEBUG [main] - ==> Parameters: 1003(Integer)
DEBUG [main] - <==      Total: 1
t3 : Team{teamId=1003, teamName='老八', location='温州', createTime=Wed Feb 17 00:00:00 CST 2021}
DEBUG [main] - ==>  Preparing: DELETE FROM `mybatis`.`team` WHERE `teamId` = ?
DEBUG [main] - ==> Parameters: 1000(Integer)
DEBUG [main] - <==    Updates: 0
DEBUG [main] - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
num : 0
DEBUG [main] - ==>  Preparing: select * from team where teamId=?
DEBUG [main] - ==> Parameters: 1003(Integer)
DEBUG [main] - <==      Total: 1
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - Returned connection 527829831 to pool.

*/

二级缓存

MyBatis 二级缓存是全局缓存,作用域超出 SqlSession 范围之外,其作用域是 mapper 的同一命名空间!

两个不同的sqlSession在同一 命名空间 下,执行的sql语句参数相同 ,最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据,从而提高性能!

应用实例

修改配置文件 mybatis.xml

···
	<settings>
	    <!--    日志配置-->
	    <setting name="logImpl" value="LOG4J"/>
	    <!--    是否启动二级缓存-->
	    <setting name="cacheEnabled" value="true"/>
	</settings>
···

修改映射文件 TeamMapper.xml

<mapper namespace="com.mapper.TeamMapper">
<!--   二级缓存标签-->
    <cache></cache>
	····    
<mapper/>

实体类实现 Serializable接口

package com.pojo;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public class Team implements Serializable {
    
    private Integer teamId;
    private String teamName;
    private String location;
    private Date createTime;
    
    //一对多的体现:一方持有多方的对象
    private List<Player> playerList1;
    private List<Player> playerList2;
  	
    //省略 set、get、toString、构造 方法
    
}

测试

import com.mapper.TeamMapper;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

public class CacheTest {
    
    @Test
    public void test02() {
        //查 数据 保留缓存
        SqlSession sqlSession1 = MybatisUtil.getSqlSession();
        Team t1 = sqlSession1.selectOne("com.mapper.TeamMapper.queryById" , 1019);
        System.out.println("t1 : " + t1);
        //清空一级缓存,保留二级缓存
        MybatisUtil.close();
    
        //测试 是否保留二级缓存
        SqlSession sqlSession2 = MybatisUtil.getSqlSession();
        Team t2 = sqlSession2.selectOne("com.mapper.TeamMapper.queryById" , 1019);
        System.out.println("t2 : " + t2);
        MybatisUtil.close();
    
        //测试 删除数据 清空二级缓存
        SqlSession sqlSession3 = MybatisUtil.getSqlSession();
        //删除不存在的数据(假删除)
        int i = sqlSession3.delete("com.mapper.TeamMapper.delById" , 10000);
        System.out.println("i : " + i);
        sqlSession3.commit();
        MybatisUtil.close();
    
        //测试 是否保留二级缓存
        SqlSession sqlSession4 = MybatisUtil.getSqlSession();
        Team t4 = sqlSession4.selectOne("com.mapper.TeamMapper.queryById" , 1019);
        System.out.println("t4 : " + t4);
        MybatisUtil.close();
        
    }
    
}

/*运行结果
    
DEBUG [main] - Created connection 292138977.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - ==>  Preparing: select * from team where teamId=?
DEBUG [main] - ==> Parameters: 1019(Integer)
DEBUG [main] - <==      Total: 1
t1 : Team{teamId=1019, teamName='哥斯拉', location='华盛顿', createTime=Sun Jul 25 00:00:00 CST 2021}
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Returned connection 292138977 to pool.
 WARN [main] - As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66
DEBUG [main] - Cache Hit Ratio [com.mapper.TeamMapper]: 0.5
t2 : Team{teamId=1019, teamName='哥斯拉', location='华盛顿', createTime=Sun Jul 25 00:00:00 CST 2021}
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 292138977 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - ==>  Preparing: DELETE FROM `mybatis`.`team` WHERE `teamId` = ?
DEBUG [main] - ==> Parameters: 10000(Integer)
DEBUG [main] - <==    Updates: 0
i : 0
DEBUG [main] - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Returned connection 292138977 to pool.
DEBUG [main] - Cache Hit Ratio [com.mapper.TeamMapper]: 0.3333333333333333
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 292138977 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - ==>  Preparing: select * from team where teamId=?
DEBUG [main] - ==> Parameters: 1019(Integer)
DEBUG [main] - <==      Total: 1
t4 : Team{teamId=1019, teamName='哥斯拉', location='华盛顿', createTime=Sun Jul 25 00:00:00 CST 2021}
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Returned connection 292138977 to pool.
    
*/
二级缓存其他设置

映射文件中的cache标签

···	
	<!--二级缓存默认配置-->
	<cache>
		<property name="eviction" value="LRU"/><!--回收策略为LRU-->
		<property name="flushInterval" value="60000"/><!--自动刷新时间间隔为60S-->
		<property name="size" value="1024"/><!--最多缓存1024个引用对象-->
		<property name="readOnly" value="true"/><!--只读-->
	</cache>
···
属性说明
eviction代表的是缓存回收策略,目前 MyBatis 提供以下策略。
LRU:使用较少,移除最长时间不用的对象;
FIFO:先进先出,按对象进入缓存的顺序来移除它们;
SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象;
WEAK:弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象
flushInterval刷新间隔时间,单位为毫秒,这里配置的是 100 秒刷新,如果省略该配置,那么只有当 SQL 被执行的时候才会刷新缓存
size引用数目,正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。这里配置的是 1024 个对象
readOnly只读,默认值为 false,意味着缓存数据只能读取而不能修改,这样设置的好处是可以快速读取缓存,缺点是没有办法修改缓存

重用cache标签配置

在命名空间中共享相同的缓存配置和实例,可以使用cache-ref 元素来引用另外一个缓存。引用实例:

···
	<!--需要指定 映射文件的位置-->
	<cache-ref namespace="com.mapper.TeamMapper" />
···

MyBatis逆向生成

Mybatis 提供逆向生成工具,该工具可以根据 数据库中的表 自动生成单表的 pojo 类、mapper 映射文件和 mapper 接口。大大缩减了开发时间!!

应用实现

库数据引入&展示 (库名mybatis)

CREATE TABLE `team` (
  `teamId` int NOT NULL AUTO_INCREMENT COMMENT '球队ID',
  `teamName` varchar(50) DEFAULT NULL COMMENT '球队名称',
  `location` varchar(50) DEFAULT NULL COMMENT '球队位置',
  `createTime` date DEFAULT NULL COMMENT '球队建立时间',
  PRIMARY KEY (`teamId`)
) ENGINE=InnoDB AUTO_INCREMENT=1026 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;


CREATE TABLE `player` (
  `playerId` int NOT NULL,
  `playerName` varchar(100) DEFAULT NULL,
  `playerNum` int DEFAULT NULL,
  `teamId` int DEFAULT NULL,
  PRIMARY KEY (`playerId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE `gamerecord` (
  `recordId` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `homeTeamId` int DEFAULT NULL COMMENT ' 主队id',
  `gameDate` datetime DEFAULT NULL COMMENT '比赛时间',
  `score` int DEFAULT NULL COMMENT '得分',
  `visitingTeamId` int DEFAULT NULL COMMENT '客队id',
  PRIMARY KEY (`recordId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

配置文件 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	
    ·····
    <dependencies>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.23</version>
        </dependency>
        <!--测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!--mybatis日志 依赖-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    	<!--xml解析 依赖-->
        <dependency>
            <groupId>org.codehaus.plexus</groupId>
            <artifactId>plexus-classworlds</artifactId>
            <version>2.5.2</version>
        </dependency>
        <!--xml解析 依赖-->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.5</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <!--反向生成插件-->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.5</version>
                <configuration>
                    <!--配置文件的路径-->
                    <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
                    <overwrite>true</overwrite>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.5</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

</project>

创建逆向生成配置文件 resources/generatorConfig.xml

注意:

  • classPathEntry标签。数据库驱动jar:添加自己的jar路径
  • jdbcConnection标签。 配置数据库连接,应用自己的 url(指定库)、账号、密码
  • javaModelGenerator标签。指定路径指定包,用于生成实体类
  • sqlMapGenerator标签。指定路径指定包,用于生成映射接口
  • javaClientGenerator标签。指定路径指定包,用于生成映射文件,执行SQL语句的
  • table标签。指定需要生成的表
<?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>
    <!--1、数据库驱动jar:添加自己的jar路径 -->
    <classPathEntry
            location="D:\Maven\repository\mysql\mysql-connector-java\8.0.23\mysql-connector-java-8.0.23.jar" />
    <context id="MyBatis" targetRuntime="MyBatis3">
        <!--去除注释 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!--2、数据库连接 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;serverTimezone=GMT"
                        userId="root"
                        password="root">
        </jdbcConnection>
        <!--
        默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer;
        为 true时把JDBC DECIMAL和NUMERIC类型解析为java.math.BigDecimal
            false: Integer
            true: BigDecimal (双精度浮点型变量double可以处理16位有效数)
         -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>
        <!--3、生成实体类 指定包名 以及生成的地址 (可以自定义地址,但是路径不存在不会自动创建
        使用Maven生成在target目录下,会自动创建) -->
        <javaModelGenerator targetPackage="com.pojo"
                            targetProject="src\main\java">
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!--4、生成SQLmapper.xml映射文件 -->
        <sqlMapGenerator targetPackage="com.mapper"
                         targetProject="src\main\resources">
        </sqlMapGenerator>
        <!--5、生成Dao(Mapper)接口文件,-->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.mapper"
                             targetProject="src\main\java">
        </javaClientGenerator>
        <!--6、要生成哪些表(更改tableName和domainObjectName就可以) -->
        <!-- tableName:要生成的表名
        enableCountByExample:Count语句中加入where条件查询,默认为true开启
        enableUpdateByExample:Update语句中加入where条件查询,默认为true开启
        enableDeleteByExample:Delete语句中加入where条件查询,默认为true开启
        enableSelectByExample:Select多条语句中加入where条件查询,默认为true开启
        selectByExampleQueryId:Select单个对象语句中加入where条件查询,默认为true开启
        -->
<!--        <table tableName="Team"-->
<!--               enableCountByExample="false"-->
<!--               enableUpdateByExample="false"-->
<!--               enableUpdateByPrimaryKey="false"-->
<!--               enableDeleteByExample="false"-->
<!--               enableDeleteByPrimaryKey="false"-->
<!--               enableSelectByExample="false"-->
<!--               selectByExampleQueryId="false">-->
<!--            <property name="useActualColumnNames" value="true"/>-->
<!--        </table>-->
        <table tableName="team">
            <!--应用 库中对应的名称 (大小写也搬过去)-->
            <property name="useActualColumnNames" value="true"/>
        </table>
        <table tableName="player">
            <property name="useActualColumnNames" value="true"/>
        </table>
        <table tableName="gamerecord">
            <property name="useActualColumnNames" value="true"/>
        </table>
    </context>
</generatorConfiguration>

Maven项目运行

非Maven项目运行

引入依赖:

  • org.codehaus.plexus:plexus-classworlds:2.5.2
  • org.mybatis.generator:mybatis-generator-core:1.3.5

在根目录下 config/generatorConfig.xml 逆向生成配置文件

启动生成代码:

package com.utils;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class Generator {
    
    public void generator() throws Exception {
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        // 指定配置文件
        File configFile = new File("src/config/generatorConfig.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
    }
    // 执行main方法以生成代码
    public static void main(String[] args) {
        try {
            Generator generatorSqlmap = new Generator();
            generatorSqlmap.generator();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
}

测试(应用测试主要部分,多余就不赘述了)

import com.mapper.TeamMapper;
import com.pojo.Team;
import com.pojo.TeamExample;
import com.utils.MybatisUtil;
import org.junit.Test;

import java.util.Date;
import java.util.List;

public class TeamTest {

    private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
    
    //查 主键
    @Test
    public void test01() {
        Team team = teamMapper.selectByPrimaryKey(1019);
        System.out.println("team : " + team);
    }
    
    //查总数 无约束
    @Test
    public void test02() {
        
        TeamExample example = new TeamExample();
        //查总数
        long l = teamMapper.countByExample(example);
        System.out.println("l : " + l);
    
    }
    
    //添加
    @Test
    public void test03() {
    
        Team team = new Team();
        team.setTeamName("bozhu-test");
        int insert = teamMapper.insert(team);
        MybatisUtil.getSqlSession().commit();
        System.out.println("insert : " + insert);
    }
    
    //动态添加
    @Test
    public void test04() {
        Team team = new Team();
        team.setTeamName("bozhu-test2");
        int insert = teamMapper.insertSelective(team);
        MybatisUtil.getSqlSession().commit();
        System.out.println("insert : " + insert);
    }
    
    //修改 指定key
    @Test
    public void test05() {
        Team team = teamMapper.selectByPrimaryKey(1026);
        team.setTeamName("老哥");
        team.setLocation("bj");
        team.setCreateTime(new Date());
        int i = teamMapper.updateByPrimaryKey(team);
        MybatisUtil.getSqlSession().commit();
        System.out.println("i : " + i);
    }
    
    //Example约束 应用
    
    /**
     * Example 约束服务
     *      criteria1容器 约束方法名
     *          add+约束列+约束方式
     *          ····
     *      OR:criteria2容器 约束方法名
     *          add+约束列+约束方式
     *          ····
     */
    @Test
    public void test06() {
        //设置多条件 服务
        TeamExample example = new TeamExample();
        //criteria 多条件 容器
        TeamExample.Criteria criteria = example.createCriteria();
        // or 添加’或‘运算符 约束
        TeamExample.Criteria criteria2 = example.or();
        //为容器添加条件
        criteria.andTeamIdBetween(1001,1100);
        criteria2.andTeamIdBetween(1,3);
        List<Team> teamList = teamMapper.selectByExample(example);
        teamList.forEach(team -> System.out.println(team));
    }
    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值