Mybatis从入门到放弃(一)

文章目录

Mybatis(一)

一. MyBatis简介

Untitled

1.1 框架概念

框架是软件的半成品,完成了软件开发中的通用操作,程序员只需少量或无需加工即可实现特定功能,从而简化开发步骤,提高效率。

1.2 常用框架

  • MVC框架:简化并强化了原先Servlet的开发步骤
  • 持久层框架:负责数据库操作
  • 胶水框架:Spring

SSM Spring SpringMVC MyBatis 技术栈

1.3 MyBatis介绍

MyBatis是一个半自动的ORM框架

ORM(Object Relational Mapping)对象关系映射,将Java中的对象与数据表中的记录对应。

ORM框架通过配置映射文件,实现对象的持久化。

  • MyBatis的前身是iBatis,iBatis是Apache软件基金会的开源项目
  • 2010年,iBatis迁移到Google Code,正式更名为MyBatis
  • 2013年迁移到GitHub托管

二. MyBatis框架部署

框架部署就是将框架引入到项目中。

2.1 创建Maven项目

2.2 在项目中添加MyBatis依赖

  • 在pom.xml中添加依赖:
<dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.10</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.32</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.26</version>
        </dependency>
    </dependencies>

2.3 创建MyBatis配置文件

  • 创建自定义模板:选择resources - 右键New - Edit File Templates
  • 在resources中创建名为db.properties的文件。
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://81.70.199.213:3306/java21?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
db.user=root
db.password=xxx

• 在resources中创建名为mybatis-config.xml的文件。
• 在mybatis-config.xml文件中配置数据库连接信息。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <properties resource="db.properties"/>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}"/>
                <property name="url" value="${db.url}"/>
                <property name="username" value="${db.user}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
    </environments>

</configuration>

三. MyBatis框架使用

案例:学生信息的数据库操作

3.1 创建数据表

CREATE TABLE `user` (
 `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
 `name` varchar(100) DEFAULT NULL,
 `email` varchar(100) DEFAULT NULL,
 `age` int(10) unsigned DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

3.2 创建实体类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

  private Long id;
  private String name;
  private String email;
  private Integer age;
}

3.3 创建Mapper接口,定义操作方法

public interface UserMapper {

  User selectUserById(Long id);
  int insertUser(User user);

}

3.4 创建mapper接口的映射文件

• 在resources目录下,新建名为mappers文件夹
• 在mappers中新建名为UserMapper.xml的映射文件(根据模板创建)
• 在映射文件中对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 namespace="com.itszt22.mybatis.mapper.UserMapper">

    <select id="selectUserById" resultType="com.itszt22.mybatis.entity.User">
        select * from user where id = #{id}
    </select>

    <insert id="insertUser" parameterType="com.itszt22.mybatis.entity.User">
        insert into user(name, email, age) values(#{name},#{email},#{age})
    </insert>

</mapper>

3.5 将映射文件添加到主配置文件 重要

修改resource下的 mybatis-config.xml文件, 添加一行语句

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <properties resource="db.properties"/>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}"/>
                <property name="url" value="${db.url}"/>
                <property name="username" value="${db.user}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>

</configuration>

四. 单元测试

4.1 添加单元测依赖

		<dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

4.2 创建单元测试类 测试代码

import com.itszt22.mybatis.dao.UserDao;
import com.itszt22.mybatis.entity.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;

public class UserDaoTest {

    //盲写
    @Test
    public void test1() throws IOException {

        //1. 读取mybatis主配置文件
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

        //2. 加载SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

        //3. 根据2.的对象 生成 SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);

        //4. 根据3.的对象生成  SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        //5. 根据4.生成Dao接口的实现类
        UserDao userDao = sqlSession.getMapper(UserDao.class);

        userDao.addUser(new User(null, "女大学生破防曲", "qwe@qq.com", 100));

        User user = userDao.getUserById(1L);
        System.out.println("user = " + user);

        //6. 关闭SqlSession
        sqlSession.close();
    }
}

Untitled

五. MyBatis的基本CRUD操作

案例:用户信息的增删查改

5.1 添加操作

<insert id="addUser" parameterType="com.itszt22.mybatis.entity.User">
	insert into user(name, email, age) values(#{name},#{email},#{age})
</insert>

5.2 删除操作

<delete id="removeUser">
        delete from user where id = #{myid}
</delete>

5.3 修改操作

根据用户id,修改其他字段信息

<update id="updateUser" parameterType="com.itszt22.mybatis.entity.User">
        update user set name=#{name},email=#{email},age=#{age}
        where id=#{id}
</update>

5.4 查询操作-查询所有

• 在UserDao接口定义操作方法
• 在User.xml中“实现”DAO中定义的方法

<select id="listAllUser" resultType="com.itszt22.mybatis.entity.User">
        select * from user
 </select>

5.6 查询操作-多参数查询

分页查询(参数 start , pageSize)多参数可以通过 param1 param2 根据传参的顺序写在sql语句中
• 在UserDao中定义操作方法,如果方法有多个参数,使用@Param注解声明参数的别名

List<User> listUserByPage(@Param("offset") int offset1,@Param("limit") int limit1);
<select id="listUserByPage" resultType="com.itszt22.mybatis.entity.User">
        select * from user
        limit #{offset},#{limit}
</select>

• 在User.xml配置sql时,使用#{别名}获取到指定的参数注意如果DAO操作方法没有通过@Param指定参数别名,在SQL中也可以通过arg0,arg1…或者param1,param2,…获取参数

5.7 查询操作-查询总记录数

无论是传入参数, 还是返回结果类型

• 在UserDao接口中定义操作方法
• 在User.xml配置sql,通过resultType指定当前操作的返回类型为int

<select id="selectCount">
        select count(*) from user
</select>

5.8 添加操作回填生成的主键

• User.xml的添加操作标签——insert

<!--    1. 主键回填第一种-->
    <insert id="addUser" parameterType="com.itszt22.mybatis.entity.User" useGeneratedKeys="true" keyProperty="id">       <!-- 这个是带主键回填的新增操作 -->
        insert into user(name, email, age) values(#{name},#{email},#{age})
    </insert>

<!--    2. 主键回填第二种 了解-->
<!--    <insert id="addUser" parameterType="com.itszt22.mybatis.entity.User">       &lt;!&ndash; 这个是带主键回填的新增操作 &ndash;>-->
<!--        <selectKey keyProperty="id" resultType="long">-->
<!--            select last_insert_id()-->
<!--        </selectKey>-->
<!--        insert into user(name, email, age) values(#{name},#{email},#{age})-->
<!--    </insert>-->

Untitled

六. 事务管理

6.1 手动提交事务

开启自动提交事务

sqlSession = sqlSessionFactory.openSession(true);

• sqlSession.commit(); 提交事务

测试类中进行事务管理

// 注意: 需要将 openSession() 中的参数去掉,或者设为 false,以关闭自动提交;
// 这样就变成手动提交
// 会影响所有 DML 操作,如果不执行提交代码,即便操作成功,也无法写入数据库
@Test
public void test5() {
    User user = new User(null, "小手一指 rap 开始", "123@qq.com", 2);
    userDao.addUser(user);

    // 在手动提交状态下,必须执行这句代码,才会真正写入数据库中
    sqlSession.commit();

    // 新增后,打印这个 user 对象,可以获取数据库中自增的 id
    System.out.println("user = " + user);
}

6.2 自动提交事务

通过 SqlSessionFactory 调用 openSession 方法获取 SqlSession 对象时,可以通过参数设置事务是否自动提交:
• 如果参数设置为 true,表示自动提交事务:factory.openSession(true);
• 如果参数设置为 false,或者不设置参数,表示手动提交:factory.openSession();/factory.openSession(false);

七. MyBatis主配置文件

mybatis-config.xml 是MyBatis框架的主配置文件,主要用于配置MyBatis数据源及属性信息。

7.1 properties标签

用于设置键值对或加载属性文件:
• 在resources目录下创建db.properties文件,配置键值对如下:

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/aDemo?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username=root
password=root

• 在mybatis-config.xml中通过properties标签引用db.properties文件。引入后,配置environment时可直接使用db.properties的key获取对应的value。

7.2 settings标签

十六、十七章节分别讲解二级缓存与延迟加载

7.3 typeAliases标签

typeAliases 中设置了一个 package 的别名扫描路径,该路径下的 Java 实体类都可以拥有一个别名(即首字母小写的类名)

7.4 plugins标签

第十章节会讲到具体的分页插件的使用。

7.5 environments标签

<!-- 在environments配置数据库连接信息 -->
<!-- 在environments标签中可以定义多个environment标签,每个environment标签可以定义一套连接配置 -->
<!-- default属性,用来指定使用哪个environment标签 -->
<environments default="mysql">

  <!-- environment 标签用于配置数据库连接信息 -->
  <environment id="mysql">

    <!--transactionManager标签用于配置数据库管理方式
      type="JDBC" 可以进行事务的提交和回滚操作
      type="MANAGED" 依赖容器完成事务管理,本身不进行事务的提交和回滚操作 -->
    <transactionManager type="JDBC"></transactionManager>

    <!--dataSource标签就是用来配置数据库连接信息 POOLED|UNPOOLED -->
    <dataSource type="POOLED">
      <property name="driver" value="${mysql_driver}"/>
      <property name="url" value="${mysql_url}"/>
      <property name="username" value="${mysql_username}"/>
      <property name="password" value="${mysql_password}"/>
    </dataSource>
  </environment>

</environments>

7.6 mappers标签

加载映射配置(映射文件、DAO注解)

<mappers>
  <mapper resource="mappers/User.xml"></mapper>
</mappers>

八. 映射文件 mapper.xml

映射文件是MyBatis框架中的核心配置文件,用于定义SQL操作和对象-关系映射。

8.1 MyBatis Mapper初始化

MyBatis解析XML文件,将标签配置封装到Java对象中。

8.2 mapper根标签

mapper文件充当mapper接口的"实现类"。namespace属性必须指定对应mapper接口的全限定名。

8.3 insert标签

用于声明插入操作。
常用属性:
• id:绑定对应DAO接口中的方法
• parameterType:指定方法参数类型(基本类型可省略)

主键回填:
• useGeneratedKeys:设置是否需要回填生成的主键
• keyProperty:指定主键回填到参数对象的哪个属性
• timeout:设置操作超时时间,默认无限等待

8.4 delete标签

用于声明删除操作。

8.5 update标签

用于声明更新操作。

8.6 select标签

用于声明查询操作。
• id:指定绑定的方法名
• parameterType:设置参数类型
• resultType:指定返回数据封装的对象类型
resultMap:指定数据表到实体类的字段映射关系
• useCache:指定是否使用缓存
• timeout:设置超时时间

8.7 resultMap标签

<!-- 定义resultMap,指定数据表字段与Java类属性的对应关系 -->
<resultMap id="userMap" type="user" autoMapping="true">
    <result property="name" column="tencent_name"/>
</resultMap>

8.8 cache标签

注意:第15章节将详细讲解缓存。

用于设置当前mapper的缓存属性:

8.9 sql和include标签

<!-- 定义常用SQL片段 -->
<sql id="userNeedColumn">
    name, age from user
</sql>

<select id="listUserByPage" resultMap="userMap">
    select
    <include refid="userNeedColumn"/>
    limit #{offset}, #{limit}
</select>

九. 分页插件

分页插件是一个独立于MyBatis框架的第三方插件。

9.1 添加分页插件依赖

PageHelper

<dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>5.3.0</version>
    </dependency>

9.2 配置插件

在MyBatis的主配置文件mybatis-config.xml中通过plugins标签进行配置:

 <plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor"/>
  </plugins>

9.3 分页示例

对查询信息进行分页查询:

@Test
public void testListStudentsByPage() {
PageHelper.startPage(2, 4);
List students = studentDAO.listStudents();
PageInfo pageInfo = new PageInfo<>(students);
// pageInfo中包含了数据及分页信息
}

带条件分页:
@Test
public void listUser() {
PageHelper.startPage(2, 3);
List users = userDao.listUserByLikeName2(“琦”);
PageInfo userPageInfo = new PageInfo<>(users);
}

十. 模糊查询:#{}和${}

案例:根据昵称模糊查询会员信息

10.1 模糊查询实现

10.1.1 DAO

public interface MemberDAO {
// 根据昵称模糊查询用户信息
// 模糊查询需使用 进行 S Q L 拼接 / / 使用 {}进行SQL拼接 // 使用 进行SQL拼接//使用{}时,即使只有一个参数也需要用@Param注解声明参数的key(非String对象参数除外)
public List searchMemberByNick(@Param(“keyWord”) String keyWord);
}

10.1.2 映射文件

SELECT member_id, member_nick, member_gender, member_age, member_city FROM members WHERE member_nick LIKE '%${keyWord}%'

使用#{}语法的两种写法:

  1. SELECT * FROM members WHERE member_nick LIKE CONCAT(‘%’, #{name}, ‘%’)
  2. SELECT * FROM members WHERE member_nick LIKE “%”#{keyWord}“%”

10.1.3 测试

@Test
public void testSearchMemberByNick() {
MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
List members = memberDAO.searchMemberByNick(“花”);
for (Member m : members) {
System.out.println(m);
}
}

10.2 #{}和${}的区别

10.2.1 #{} 占位符

语法:#{字符}

MyBatis处理#{}使用PreparedStatement对象:

SELECT * FROM user WHERE id = #{userId}

MyBatis创建PreparedStatement对象,执行SQL语句:
String sql = “SELECT * FROM user WHERE id = ?”;
PreparedStatement pst = conn.prepareStatement(sql);
pst.setInt(1, 1L); // 传递参数
ResultSet rs = pst.executeQuery(); // 执行SQL语句

#{}特点:

  1. 使用PreparedStatement对象,执行SQL语句,效率高。
  2. 能避免SQL注入,执行更安全。
  3. 常用作列值,位于等号右侧,其值与数据类型相关。

10.2.2 ${} 占位符

语法:${字符}

MyBatis执行${}占位符的SQL语句:

SELECT * FROM student WHERE id = ${studentId}

表示字符串连接,将 S Q L 语句的其他内容与 {} 表示字符串连接,将SQL语句的其他内容与 表示字符串连接,将SQL语句的其他内容与{}内容用字符串(+)连接:
String sql = “SELECT * FROM student WHERE id=” + “1001”;

MyBatis创建Statement对象,执行SQL语句:
Statement stmt = conn.createStatement(sql);
ResultSet rs = stmt.executeQuery();

${}特点:

  1. 使用Statement对象,执行SQL语句,效率较低。
  2. 使用字符串连接方式,存在SQL注入风险,有代码安全问题。
  3. 数据原样使用,不区分数据类型。
  4. 常用作表名或列名,通常在等号左侧。仅在能保证数据安全的情况下使用${}。

十一. MyBatis日志配置

MyBatis作为一个封装良好的ORM框架,其执行过程不易追踪。为了帮助开发人员了解MyBatis的工作流程以及每个步骤完成的任务,该框架支持log4j日志系统。通过配置MyBatis的日志设置,我们可以在运行时查看详细的日志信息。

11.1 添加日志框架依赖

我们将使用log4j作为日志框架:

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

11.2 添加日志配置文件

• 在resources目录下创建名为log4j.properties的文件
• 在log4j.properties文件中配置日志输出方式
以下是日志输出级别及输出方式的配置示例:

log4j.rootLogger=DEBUG,stdout
# MyBatis logging configuration...
#log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 定义日志的打印格式 %t 表示线程名称 %5p 日志级别 %msg日志信息
log4j.appender.stdout.layout.ConversionPattern=[%t] %5p - %msg :%m%n

11.3 日志信息的级别

日志框架根据信息的重要程度将日志分为5个级别:

级别 说明
DEBUG 输出调试信息
INFO 输出提示信息
WARN 输出警告信息
ERROR 输出一般性错误信息
FATAL 输出致命性错误信息

十二. 关联映射(复杂且重要, 掌握基本写法即可)

12.1 实体关系

实体关系指数据实体之间的关联,如用户和角色、房屋和楼栋、订单和商品。实体关系分为四种:

一对一关联
实例:人和身份证、学生和学生证、用户基本信息和详情
数据表关系:
• 主键关联(用户表主键与详情主键相同时,表示匹配数据)
• 唯一外键关联

一对多关联多对一关联
实例:
• 一对多:班级和学生、类别和商品、楼栋和房屋
• 多对一:A班学生和A班级、橱柜椅子商品和家具类别
数据表关系:
• 在"多"的一端添加外键与"一"的一端关联

多对多关联
实例:用户和角色、角色和权限、房屋和业主、学生和社团、订单和商品
数据表关系:建立第三张关系表,添加两个外键分别与两张表主键关联
例:用户(user_id) — 用户角色表(uid,rid) — 角色(role_id)

12.2 一对一关联

实例:用户—详情

12.2.1 创建数据表

– 用户信息表

CREATE TABLE users (
	user_id INT auto_increment NOT NULL,
	user_name VARCHAR(100) NOT NULL,
	user_pwd varchar(100) NOT NULL,
	user_realname varchar(100) NOT NULL,
	user_img varchar(100) NOT NULL,
	CONSTRAINT users_pk PRIMARY KEY (user_id),
	CONSTRAINT users_un UNIQUE KEY (user_name)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_general_ci;

– 用户详情表

CREATE TABLE details (
	detail_id INT auto_increment NOT NULL,
	user_addr varchar(100) NOT NULL,
	user_tel CHAR(11) NOT NULL,
	user_desc varchar(100) NULL,
	uid INT NOT NULL,
	CONSTRAINT details_pk PRIMARY KEY (detail_id),
	CONSTRAINT details_un UNIQUE KEY (uid),
	CONSTRAINT details_FK FOREIGN KEY (uid) REFERENCES users(user_id)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_general_ci;

12.2.2 创建实体类

• Users

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Users {
  private int userId;
  private String userName;
  private String userPwd;
  private String userRealname;
  private String userImg;
}

Detail

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Detail {
  private int detailId;
  private String userAddr;
  private String userTel;
  private String userDesc;
  private int uid;
}

12.2.3 添加操作(事务的重要性)

为这两个实体类各写一个新增方法, 并后面那个方法故意报错一下, 如果是自动提交, 发现只有一个入库成功, 对于用户来说, 这将非常困惑, 成也不是, 失败也不是

解决方案: 这两个行为本来就是一体的, 要么一起成功, 要么一起失败, 通过开启手动提交, 最后commit, 如果捕获到了异常,在catch中调用rollback清空之前的临时提交记录.

Untitled

12.2.4 一对一关联查询

在查询用户的同时关联查询出与之对应的详情实体映射文件子查询

Untitled

Untitled

Untitled

12.3 一对多关联

案例:班级(1)—学生(n)

12.3.1 创建数据表

– 创建班级信息表

create table classes(
  cid int primary key auto_increment,
  cname varchar(30) not null unique,
  cdesc varchar(100)
);

– 创建学生信息表

create table students(
  sid char(5) primary key,
  sname varchar(20) not null,
  sage int not null,
  scid int not null
);

12.3.2 创建实体类

INSERT INTO classes (cname,cdesc) VALUES
	 ('养老','不加班'),
	 ('996','加班');

INSERT INTO students (sid,sname,sage,scid) VALUES
	 ('20221','wz',100,21),
	 ('20222','lcc',100,20),
	 ('20223','kr',100,21);
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Clazz {
    private int classId;
    private String className;
    private String classDesc;
    private List stus;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
  private String stuId;//学号
  private String stuName;
  private int stuAge;
  private int stucid; //学生所在班级的id
}
<?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 namespace="com.itszt22.mybatis.dao.ClassDao">

<!--    <resultMap id="classMap" type="clazz">-->
<!--        <id property="classId" column="cid"/>-->
<!--        <result property="className" column="cname"/>-->
<!--        <result property="classDesc" column="cdesc"/>-->

<!--        <collection property="stus" ofType="student">-->
<!--            <id property="stuId" column="sid"/>-->
<!--            <result property="stuName" column="sname"/>-->
<!--            <result property="stuAge" column="sage"/>-->
<!--            <result property="stucid" column="scid"/>-->
<!--        </collection>-->
<!--    </resultMap>-->

<!--    <select id="getClazzById" resultMap="classMap">-->
<!--        select * from classes-->
<!--        inner join students on classes.cid = students.scid-->
<!--        where cid = #{cid}-->
<!--    </select>-->

</mapper>

12.3.3 关联查询

当查询一个班级的时候, 要关联查询出这个班级下的所有学生
连接查询
连接查询映射配置




–i>
Clazz对象的stus属性是一个List集合,需要使用colLection标签
<-
coLLection标签的ofType属性声明集合中元素的类型





Untitled

12.4 多对一关联

实例:学生(n)—班级(1)
当查询一个学生的时候,关联查询这个学生所在的班级信息

12.4.1 创建实体类

Untitled

Untitled

12.4.2 关联查询

Untitled

12.5 多对多关联 作业

案例:学生(m)—课程(n)

12.5.1 创建数据表

– 学生信息表(如上)
– 课程信息表
create table courses(
course_id int primary key auto_increment,
course_name varchar(50) not null
);
– 选课信息表/成绩表(学号、课程号、成绩) 中间关系表
create table grades(
sid bigint not null,
cid int not null,
score int not null
);

12.5.2 关联查询

查询学生时,同时查询学生选择的课程根据课程编号查询课程时,同时查询选择了这门课程的学生连接查询映射配置

Untitled

Untitled

Untitled

十三. 动态SQL(重要)

MyBatis的独特特征:动态SQL,这是国外的JPA无法实现的

交友网站如珍爱网、百合网允许用户筛选心仪对象,可选条件包括性别、年龄、城市和身高。

电商平台如淘宝、京东则让顾客筛选商品,例如羽毛球拍的品牌和价格。

不同用户的筛选条件各异,导致执行的SQL语句也不尽相同:

搜索女性:SELECT * FROM members WHERE gender=‘女’
搜索18-23岁女性:SELECT * FROM members WHERE gender=‘女’ AND age >= 18 AND age <= 23
按年龄和城市搜索:SELECT * FROM members WHERE age >= 18 AND age <= 23 AND city=‘’

尽管我们可以穷举所有可能的条件组合来编写SQL,但这种方法既繁琐又复杂。

MyBatis通过提供动态SQL配置,优雅地解决了多条件查询的难题。

13.1 什么是动态SQL?

根据查询条件动态完成SQL的拼接

13.2 动态SQL使用案例

案例:心仪对象搜索

13.2.1 创建数据表

create table members(
  member_id int primary key auto_increment,
  member_nick varchar(20) not null unique,
  member_gender char(2) not null,
  member_age int not null,
  member_city varchar(30) not null
);

13.2.2 创建实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Member {
  private int memberId;
  private String memberNick;
  private String memberGender;
  private int memberAge;
  private String memberCity;
}

13.2.3 创建DAO接口

在DAO接口中定义一个多条件查询的方法

import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface MemberMapper {

    List<Member> listMember(@Param("age") Integer age,@Param("city") String city);

    void updateMember(Member member);
}

13.3 if

<!--        1. if写法, 解决不了 一些特殊符号多出的问题  -->
<!--        <if test="age != null">-->
<!--            and member_age=#{age}-->
<!--        </if>-->
<!--        <if test="city != null">-->
<!--            and member_city=#{city}-->
<!--        </if>-->

测试

@Test
public void testSearchMember() {

  HashMap<String,Object> params = new HashMap<String, Object>();
  params.put("gender","女");
  params.put("minAge",18);
  //params.put("maxAge",23);
  params.put("city","武汉");

  //-----------------------------------------------------------------------
  MemberSearchCondition params2 = new MemberSearchCondition();
  params2.setGender("女");
  //params2.setMinAge(21);
  //params2.setMaxAge(30);
  //params2.setCity("武汉");

  //==========================================================================

  MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
  List<Member> members = memberDAO.searchMember(params2);

  for (Member m: members) {
    System.out.println(m);
  }
}

13.4 set

<update id="updateMember" parameterType="member">
        update members
        <set>
            <if test="memberGender != null">
                member_gender=#{memberGender},
            </if>
            <if test="memberAge != null">
                member_age=#{memberAge},
            </if>

            <if test="memberNick != null">
                member_nick=#{memberNick},
            </if>

            <if test="memberCity != null">
                member_city=#{memberCity}
            </if>
        </set>
        where member_id=#{memberId}
</update>

13.5 where

<select id="listMember" resultMap="memberMap">
        select * from members
<!--        1. if写法, 解决不了 一些特殊符号多出的问题  -->
<!--        <if test="age != null">-->
<!--            member_age=#{age} and-->
<!--        </if>-->
<!--        <if test="city != null">-->
<!--             member_city=#{city}-->
<!--        </if>-->

<!--        2. where写法 自动发现有and or等连接词汇的, 如果多余, 自动去除-->
        <where>
            <if test="age != null">
                and member_age=#{age}
            </if>
            <if test="city != null">
                and member_city=#{city}
            </if>
        </where>

    </select>

13.6 foreach

当一次性批量插入的数据超过10w条的时候, 应该使用jdbc原始的addBatch语法

如果一次性批量插入数据在10w以下的时候, 使用哪个都ok

insert into tableA values(d1, d2), (d11, d22), (d111, d222)
public interface MemberDAO {
  //查询指定城市的会员
  public List<Member> searchMemberByCity(List<String> cities);
}
<select id="searchMemberByCity" resultMap="memberMap">
  select member_id,member_nick,member_gender,member_age,member_city
  from members where member_city in
  <foreach collection="list" item="cityName" separator="," open="(" close=")">
    #{cityName}
  </foreach>
</select>

测试

@Test
public void searchMemberByCity() {
  List<String> cities = new ArrayList<String>();
  cities.add("厦门");
  cities.add("宜昌");
  MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
  List<Member> members = memberDAO.searchMemberByCity(cities);
  for (Member m: members) {
    System.out.println(m);
  }
}

十四. 配置数据库连接池 - 整合Druid

MyBatis作为一个ORM框架,在进行数据库操作时需要与数据库建立连接。MyBatis支持基于数据库连接池的连接创建方式。
当我们配置MyBatis数据源时,只要将dataSource标签的type属性值设为POOLED,就可以使用MyBatis内置的连接池管理连接。
如果我们想要使用第三方的数据库连接池,则需进行自定义配置。

14.1 常见的连接池

• DBCP
• C3P0
• Druid:性能较好,提供便捷的监控系统
• HikariCP:性能最佳

功能比较:
• PSCache支持:DBCP、Druid、C3P0均支持
• 监控:DBCP (JMX)、Druid (JMX/log/http)、C3P0 (JMX,log)、HikariCP (JMX)
• 扩展性:Druid好,其他较弱
• SQL拦截及解析:仅Druid支持
• 代码复杂度:DBCP简单,Druid中等,C3P0复杂,HikariCP简单
• 特点:

  • DBCP:依赖于commons-pool
  • Druid:阿里开源,功能全面
  • C3P0:历史悠久,代码逻辑复杂,维护困难
  • HikariCP:优化力度大,功能简单,源于BoneCP
    • 连接池管理:
  • DBCP:LinkedBlockingDeque
  • Druid:数组
  • HikariCP:threadlocal + CopyOnWriteArrayList

14.2 添加Druid依赖

<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid</artifactId>
   <version>1.2.16</version>
</dependency>

14.3 创建Druid连接池工厂

public class DruidDataSourceFactory extends PooledDataSourceFactory {
  public DruidDataSourceFactory() {
    this.dataSource = new DruidDataSource();
  }
}

14.4 将DruidDataSourceFactory配置给MyBatis数据源

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC"/>
    <dataSource type="com.itszt22.mybatis.DruidDataSourceFactory">
      <property name="driverClass" value="${driver}"/>
      <property name="jdbcUrl" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
  </environment>
</environments>

十五. MyBatis的注解开发(仅供了解)

15.1 MyBatis的常用注解

近年来,注解开发日益流行。MyBatis也支持注解开发方式,这可以减少Mapper映射文件的编写。我们将先学习基本的CRUD操作,然后深入复杂的多表映射操作。

常用注解:
• @Insert:实现新增
• @Update:实现更新
• @Delete:实现删除
• @Select:实现查询
• @Result:实现结果集封装
• @Results:与@Result配合使用,封装多个结果集
• @One:实现一对一结果集封装
• @Many:实现一对多结果集封装

15.2 MyBatis的增删改查

我们将完成user表的基本增删改查操作。

修改MyBatis的核心配置文件。由于我们使用注解替代了映射文件,只需加载使用了注解的Mapper接口:

或者指定扫描包含映射关系的接口所在的包:

注意:package扫包用于通过注解实现接口,而mapper是通过XML实现接口。接口只能有一种实现方式,如果两者都存在,会报错:“重复注册,already registered”。

15.3 MyBatis的注解实现复杂映射开发

在实现复杂关系映射时,我们之前在映射文件中使用配置。使用注解开发后,我们可以组合@Results、@Result、@One和@Many注解来完成复杂关系的配置。

15.4 一对一查询

15.4.1 一对一查询的模型
用户表和订单表的关系:一个用户有多个订单,一个订单只属于一个用户。
一对一查询需求:查询一个订单,同时查询出该订单所属的用户。

15.4.2 一对一查询的语句
对应的SQL语句:
select * from orders;
select * from user where id = 查询出订单的uid;

15.4.3 创建Order和User实体
public class Order {
private int id;
private Date ordertime;
private double total;
// 代表当前订单所属的客户
private User user;
}

public class User {
private int id;
private String username;
private String password;
private Date birthday;
}

15.4.4 创建OrderMapper接口
public interface OrderMapper {
List findAll();
}

15.4.5 使用注解配置Mapper
public interface OrderMapper {
@Select(“select * from orders”)
@Results({
@Result(id = true, property = “id”, column = “id”),
@Result(property = “ordertime”, column = “ordertime”),
@Result(property = “total”, column = “total”),
@Result(property = “user”, column = “uid”,
javaType = User.class,
one = @One(select = “com.itszt.mapper.UserMapper.findById”))
})
List findAll();
}

public interface UserMapper {
@Select(“select * from user where id = #{id}”)
User findById(int id);
}

15.4.6 测试结果
@Test
public void testSelectOrderAndUser() {
List all = orderMapper.findAll();
for (Order order : all) {
System.out.println(order);
}
}

15.5 一对多查询

15.5.1 一对多查询的模型
用户表和订单表的关系:一个用户有多个订单,一个订单只属于一个用户。
一对多查询需求:查询一个用户,同时查询出该用户拥有的所有订单。

15.5.2 一对多查询的语句
对应的SQL语句:
select * from user;
select * from orders where uid = 查询出用户的id;

15.5.3 修改User实体
public class Order {
private int id;
private Date ordertime;
private double total;
// 代表当前订单所属的客户
private User user;
}

public class User {
private int id;
private String username;
private String password;
private Date birthday;
// 代表当前用户拥有的订单
private List orderList;
}

15.5.4 创建UserMapper接口
List findAllUserAndOrder();

15.5.5 使用注解配置Mapper
public interface UserMapper {
@Select(“select * from user”)
@Results({
@Result(id = true, property = “id”, column = “id”),
@Result(property = “username”, column = “username”),
@Result(property = “password”, column = “password”),
@Result(property = “birthday”, column = “birthday”),
@Result(property = “orderList”, column = “id”,
javaType = List.class,
many = @Many(select = “com.itszt.mapper.OrderMapper.findByUid”))
})
List findAllUserAndOrder();
}

public interface OrderMapper {
@Select(“select * from orders where uid = #{uid}”)
List findByUid(int uid);
}

15.5.6 测试结果
List all = userMapper.findAllUserAndOrder();
for (User user : all) {
System.out.println(user.getUsername());
List orderList = user.getOrderList();
for (Order order : orderList) {
System.out.println(order);
}
System.out.println(“-----------------------------”);
}

15.6 多对多查询

15.6.1 多对多查询的模型
用户表和角色表的关系:一个用户有多个角色,一个角色被多个用户使用。
多对多查询需求:查询用户同时查询出该用户的所有角色。

15.6.2 多对多查询的语句
对应的SQL语句:
select * from user;
select * from role r, user_role ur where r.id = ur.role_id and ur.user_id = 用户的id

15.6.3 创建Role实体,修改User实体
public class User {
private int id;
private String username;
private String password;
private Date birthday;
// 代表当前用户拥有的订单
private List orderList;
// 代表当前用户拥有的角色
private List roleList;
}

public class Role {
private int id;
private String rolename;
}

15.6.4 添加UserMapper接口方法
List findAllUserAndRole();

15.6.5 使用注解配置Mapper
public interface UserMapper {
@Select(“select * from user”)
@Results({
@Result(id = true, property = “id”, column = “id”),
@Result(property = “username”, column = “username”),
@Result(property = “password”, column = “password”),
@Result(property = “birthday”, column = “birthday”),
@Result(property = “roleList”, column = “id”,
javaType = List.class,
many = @Many(select = “com.itszt.mapper.RoleMapper.findByUid”))
})
List findAllUserAndRole();
}

public interface RoleMapper {
@Select(“select * from role r, user_role ur where r.id = ur.role_id and ur.user_id = #{uid}”)
List findByUid(int uid);
}

15.6.6 测试结果
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List all = mapper.findAllUserAndRole();
for (User user : all) {
System.out.println(user.getUsername());
List roleList = user.getRoleList();
for (Role role : roleList) {
System.out.println(role);
}
System.out.println(“----------------------------------”);
}

动态SQL注解示例
@Select("”)
@Results({
@Result(id = true, property = “id”, column = “id”),
@Result(property = “name”, column = “name”),
@Result(property = “email”, column = “email”),
@Result(property = “password”, column = “password”)
})
List findUsersByCondition(String email, String name);

十六. 总结

  • MyBatis是一个半自动的ORM框架,简化了Java对象与数据库表之间的映射
  • 框架部署包括添加依赖、创建配置文件和映射文件
  • MyBatis支持基本的CRUD操作,可以通过XML或注解方式实现
  • 动态SQL允许根据不同条件动态生成SQL语句,提高了查询的灵活性
  • MyBatis支持一对一、一对多、多对多等复杂关系映射
  • 可以集成第三方连接池如Druid来优化数据库连接管理
  • MyBatis提供了缓存机制和延迟加载功能,可以提高查询性能
  • 注解开发方式可以减少XML配置,但对于复杂查询,XML配置方式更加灵活
  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

繁星-赵老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值