MyBatis框架知识点总结

news2025/1/13 13:18:24

一、引言


1.1 什么是框架?

框架:框架使用你的,而不是你在使用框架的。

框架让我们提供什么信息,配置信息,数据库连接用户名密码等,你必须提供,还得按照框架要要求的方式提供,否则你就别用它。

软件的半成品,解决了软件开发过程当中的普适性问题,从而 简化了开发步骤,提供了开发的效率

怎么写简历?下载一个简历的模板,就是一个简历的半成品,我们只需要到模板中填入数据【个人信息】就可以.

MyBatis: 只需要按框架的要求的给你数据库连接信息,提供一个Dao层接口,写一个SQL语句,就OK。

1.2 什么是ORM框架?

ORM(Object Relational Mapping)对象关系映射,将程序中的 一个对象与表中的一行数据一一对应
ORM框架提供了持久化类与表的映射关系,在运行时参照映射文件的信息, 把对象持久化到数据库中`

1.3 使用JDBC完成ORM操作的缺点?

存在大量的冗余代码。
手工创建 Connection、Statement 等。
手工将结果集封装成实体对象。
查询效率低,没有对数据访问进行过优化(Not Cache)。

二、MyBatis框架

2.1 概念

小鸟:轻量级简单灵活的框架。

MyBatis本是Apache软件基金会的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了Google Code,并且改名为MyBatis 。2013年11月迁移到Github。
MyBatis是一个 优秀的基于Java的持久层框架,支持自定义SQL,存储过程和高级映射。
MyBatis 对原有JDBC操作进行了封装,几乎消除了所有JDBC代码,使开发者只需关注 SQL 本身。
MyBatis可以使用简单的XML或Annotation来配置执行SQL,并 自动完成ORM操作,将执行结果返回。

2.2 访问与下载

官方网站: http://www.mybatis.org/mybatis-3/(opens new window)
下载地址: https://github.com/mybatis/mybatis-3/releases/tag/mybatis-3.5.1(opens new window)

三、构建Maven项目

3.1 新建项目

使用IDEA打开已创建的文件夹目录

3.2 选择Maven目录

选择Maven项目

3.3 GAV坐标

GAV坐标

四、MyBatis环境搭建【重点】

4.1 pom.xml中引入MyBatis核心依赖

在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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.qf</groupId>
    <artifactId>hello-mybatis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>


    <dependencies>
        <!-- 添加mybatis的依赖 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>

        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
    </dependencies>

</project>

4.2 创建MyBatis配置文件

数据库连接配置文件 注意修改下properties属性文件的编码
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/db2202?useUnicode=true&characterEncoding=UTF-8
username=root
password=root
initialSize=10
maxActive=20
minIdle=5
maxWait=3000
removeAbandoned=true
removeAbandonedTimeout=3000
创建并配置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>
    <properties resource="database.properties"/>
    <!--JDBC环境配置、选中默认环境-->
    <environments default="dev">
        <environment id="dev">
            <!--事务管理-->
            <transactionManager type="JDBC"/>
            <!--连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--Mapper注册-->
    <mappers>
        <mapper resource="mapper/UserMapper.xml" />
    </mappers>
    
</configuration>
  • 注意:mapper.xml默认建议存放在resources中,路径不能以/开头

扩展点:

IDEA的全局配置,新项目配置

五、MyBatis开发步骤【重点】

5.1 建表

create table users(
  id int primary key auto_increment,
  name varchar(50),
  password varchar(50),
  sex varchar(1),
  birthday datetime,
  registTime datetime
)default charset = utf8;

5.2 定义实体类

定义所需CURD操作的实体类
package com.qf.mybatis.part1.basic;

public class User {
    private Integer id;
    private String name;
    private String password;
    private String sex;
      private Date birthday;
      private Date registTime;

    //Getters And Setters
}

5.3 定义DAO接口

根据所需DAO定义接口、以及方法
package com.qf.hello.mybatis.dao;

import com.qf.hello.mybatis.entity.User;

public interface UserMapper {
    // 根据ID查询用户
    User selectUserById(Integer id);
}

5.4 编写Mapper.xml

在resources目录中下新建mapper文件夹
在文件夹下创建UserMapper.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">
<mapper namespace="com.qf.hello.mybatis.dao.UserMapper">
    <select id="selectUserById" parameterType="int" resultType="com.qf.hello.mybatis.entity.User">
        select * from users where id=#{id}
    </select>
</mapper>

安装个mybatis的插件

5.5 注册Mapper

将Mapper.xml注册到mybatis-config.xml中
<!--Mapper文件注册位置-->
<mappers>
    <!--注册Mapper文件-->
    <mapper resource="mapper/UserMapper.xml"/>
</mappers>

5.6 测试一

MyBatis的API操作方式
package com.qf.hello.mybatis.dao;

import com.qf.hello.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.jupiter.api.Test;

import java.io.InputStream;

import static org.junit.jupiter.api.Assertions.*;

class UserMapperTest {

    @Test
    void selectUserById() {
        try {
            // 1、读取mybatis的配置文件
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            // 2、创建SqlSession会话,mybatis使用工厂建造者的方式创建
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
            SqlSession sqlSession = factory.openSession();
            // 3、获取要执行操作的接口
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            // 4、执行查询
            User user = userMapper.selectUserById(1);
            // 5、查看数据
            System.out.println(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

5.7 测试二【了解】

iBatis传统操作方式
package com.qf.mybatis.part1.basic;

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 HelloMyBatis {

    @Test
    public void test2() throws IOException {
        //1.获得读取MyBatis配置文件的流对象
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");

        //2.构建SqlSession连接对象的工厂
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);

        //3.通过工厂获得连接对象
        SqlSession sqlSession = factory.openSession();

        //4.通过连接对象直接调用接口中的方法
        Object o = sqlSession.selectOne("com.qf.mybatis.part1.basic.UserDao.selectUserById", 1);
      
          System.out.println(o);
    }
}

六、细节补充

6.1 解决mapper.xml存放在resources以外路径中的读取问题

在pom.xml文件最后追加< build >标签,以便可以将xml文件复制到classes中,并在程序运行时正确读取。
<build>
     <!--指定资源文件的位置-->
     <resources>
         <resource>
             <directory>src/main/java</directory>
             <includes>
                 <include>**/*.xml</include>
             </includes>
         </resource>

         <resource>
             <directory>src/main/resources</directory>
             <includes>
                 <include>**/*.xml</include>
                 <include>**/*.properties</include>
             </includes>
         </resource>
     </resources>
</build>

6.2 properties配置文件

对于mybatis-config.xml的核心配置中,如果存在需要频繁改动的数据内容,可以提取到properties中。
#database.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/example?useUnicode=true&characterEncpding=utf8
jdbc.username=root
jdbc.password=123456
修改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配置文件路径(外部配置、动态替换)-->
    <properties resource="database.properties" />

    <environments default="MySqlDB">
        <environment id="MySqlDB">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--使用$ + 占位符-->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

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

6.3 类型别名

为实体类定义别名,提高书写效率。
<?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 ... />
    
    <!--定义别名二选一-->
    <typeAliases>
        <!--定义类的别名-->
        <typeAlias type="com.qf.mybatis.part1.basic.User" alias="user" />
        
        <!--自动扫描包,将原类名作为别名-->
        <package name="com.qf.hello.mybatis.entity"/>
    </typeAliases>
  
      ...
</configuration>

6.4 创建log4j配置文件

pom.xml添加log4j依赖
<!-- log4j日志依赖 https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
创建并配置log4j.properties
# Global logging configuration
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
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

级别

描述

ALL LEVEL

打开所有日志记录开关;是最低等级的,用于打开所有日志记录。

DEBUG

输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的。

INFO

输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。

WARN

输出警告信息;表明会出现潜在错误的情形。

ERROR

输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。

FATAL

输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。

OFF LEVEL

关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。

七、MyBatis的CRUD操作【重点】

7.1 查询

标签:< select id="" resultType="" >

7.1.1 注解参数绑定【推荐】

import org.apache.ibatis.annotations.Param; //引入注解

public interface UserMapper {
    // 根据名字和密码查询用户,如果是多参数入参推荐使用@Param指定参数名
    User selectUserByNameAndPwd(@Param("name") String name, @Param("password") String pwd);
}
<!--  多参数入参在接口中使用@Param注解进行参数绑定  --><selectid="selectUserByNameAndPwd"resultType="user">
    select *
    from users
    where name = #{name}
    and password = #{password}
</select>

7.1.2 Map参数绑定

public interface UserMapper {
    // 根据名字和密码查询用户,传递map类型的参数
    User selectUserByNameAndPwdWithMap(Map<String,String> map);
}
Map<String, String> map = new HashMap<>();
map.put("name", "刘阳");
map.put("pwd", "123456");
User user = userMapper.selectUserByNameAndPwdWithMap(map);
<!-- 参数如果传递的是map 占位符就是map中的key -->
<select id="selectUserByNameAndPwdWithMap" resultType="user" parameterType="map">
    select *
    from users
    where name = #{name}
    and password = #{pwd}
</select>

7.1.3 对象参数绑定

public interface UserMapper {
    //使用对象属性进行参数绑定
    // 根据名字和密码查询用户,传递user类型的参数
    User selectUserByNameAndPwdWithUser(User user);
}
<!--    如果参数传递的是对象,那么占位符中就是对象的属性-->
<select id="selectUserByNameAndPwdWithUser" resultType="user" parameterType="user">
    select *
    from users
    where name = #{name}
    and password = #{password}
</select>

7.1.4 模糊查询

public interface UserMapper {
    // 根据用户名字模糊查询用户
    User selectUserByName(String name);
}
<select id="selectUserByName" resultType="user" parameterType="string">
    select * from users where name like concat('%',#{name},'%')
</select>

7.2 删除

标签:< delete id="" parameterType="" >
<delete id="deleteUser" parameterType="int">
    DELETE FROM users
    WHERE id = #{id} <!--只有一个参数时,#{任意书写}-->
</delete>

7.3 修改

标签:< update id="" parameterType="" >
<update id="updateUser" parameterType="user">
    UPDATE users SET name=#{name}, password=#{password}, sex=#{sex}, birthday=#{birthday}
    WHERE id = #{id} <!--方法参数为对象时,可直接使用#{属性名}进行获取-->
</update>

7.4 添加

标签:< insert id="" parameterType="" >
<!--手动主键-->
<insert id="insertUser" parameterType="user">
    insert into users values (#{id},#{name},#{password},#{sex},#{birthday},#{registTime})
</insert>

<!--自动主键-->
<insert id="insertUser" parameterType="user">
    <!-- 自动增长主键,以下两种方案均可 -->
    insert into users values (#{id},#{name},#{password},#{sex},#{birthday},#{registTime})
    insert into users values (NULL,#{name},#{password},#{sex},#{birthday},#{registTime})
</insert>

7.5 主键回填

标签:< selectKey id="" resultType="" order="AFTER|BEFORE">

7.5.1 通过last_insert_id()查询主键

<!--新增后回填ID-->
<insert id="insertUserWithId" parameterType="user">
    <selectKey keyProperty="id" resultType="int" order="AFTER">
        select last_insert_id()
    </selectKey>
    insert into users values (#{id},#{name},#{password},#{sex},#{birthday},#{registTime});
</insert>

7.5.2 通过uuid()查询主键

SELECT uuid();
-- replace 替换函数
select replace('大壮','大','小');
-- 1:原始字串 2:需要替换的内容 3:替换成什么内容
SELECT replace(uuid(),'-','');
create table `order`(
  id varchar(32) primary key, # 字符型主键
  name varchar(50)
)default charset = utf8;
class Order{
    private String id;
    private String name;
    //set+get ...
}
<!-- 先通过uuid生成主键 然后使用这个主键新增order -->
<insert id="insertOrder" parameterType="order">
    <selectKey order="BEFORE" resultType="string" keyProperty="id"><!-- 插入之前 -->
        SELECT replace(uuid(),'-','')<!-- 适用于字符类型主键 -->
    </selectKey>
    insert into `order` values (#{id},#{name})
</insert>

八、MyBatis工具类【重点】

8.1 封装工具类

Resource:用于获得读取配置文件的IO对象,耗费资源,建议通过IO一次性读取所有所需要的数据。
SqlSessionFactory:SqlSession工厂类,内存占用多,耗费资源,建议每个应用只创建一个对象。
SqlSession:相当于Connection,可控制事务,应为线程私有,不被多线程共享。
将获得连接、关闭连接、提交事务、回滚事务、获得接口实现类等方法进行封装。
package com.qf.mybatis.part1.utils;

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 java.io.InputStream;

public class MyBatisUtils {

      //获得SqlSession工厂
    private static SqlSessionFactory factory;

      //创建ThreadLocal绑定当前线程中的SqlSession对象
    private static final ThreadLocal<SqlSession> tl = new ThreadLocal<SqlSession>();

    static {
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            factory = new SqlSessionFactoryBuilder().build(is);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获得连接(从tl中获得当前线程SqlSession)
    private static SqlSession openSession(){
        SqlSession session = tl.get();
        if(session == null){
            session = factory.openSession();
            tl.set(session);
        }
        return session;
    }

    //释放连接(释放当前线程中的SqlSession)
    private static void closeSession(){
        SqlSession session = tl.get();
        session.close();
        tl.remove();
    }

    //提交事务(提交当前线程中的SqlSession所管理的事务)
    public static void commit(){
        SqlSession session = openSession();
        session.commit();
        closeSession();
    }

    //回滚事务(回滚当前线程中的SqlSession所管理的事务)
    public static void rollback(){
        SqlSession session = openSession();
        session.rollback();
        closeSession();
    }

    //获得接口实现类对象
    public static <T extends Object> T getMapper(Class<T> clazz){
        SqlSession session = openSession();
        return session.getMapper(clazz);
    }
}

8.2 测试工具类

调用MyBatisUtils中的封装方法。
@Test
public void testUtils() {
    try {
                UserDao userDao = MyBatisUtils.getMapper(UserDao.class);
                userDao.deleteUser(15);
                MyBatisUtils.commit();
        } catch (Exception e) {
                MyBatisUtils.rollback();
                e.printStackTrace();
        }
}

九、ORM映射【重点】


对象关系映射:

类 ----> 表
类的属性--->字段
类的一个实例 ---> 表中的一条数据

9.1 MyBatis自动ORM失效

MyBatis只能自动维护库表”列名“与”属性名“相同时的一一对应关系,二者不同时,无法自动ORM。

自动ORM失效

create table t_manager(
    mgr_id int primary key auto_increment comment '主键',
    mgr_name varchar(20) not null comment '名字',
    mgr_pwd varchar(20) not null comment '密码'
)default charset='utf8';

show tables ;

desc t_manager;

insert into t_manager values(NULL,'杜猛','123');

select * from t_manager where mgr_id=1;

9.2 方案一:列的别名

在SQL中使用 as 为查询字段添加列别名,以匹配属性名。
<mapper namespace="com.qf.mybatis.part2.orm.ManagerDao">
    <select id="selectManagerByIdAndPwd" resultType="com.qf.mybatis.part2.orm.Manager">
        SELECT mgr_id AS id , mgr_name AS username , mgr_pwd AS password
        FROM t_managers
        WHERE mgr_id = #{id} AND mgr_pwd = #{pwd}
    </select>
</mapper>

9.3 方案二:结果映射(ResultMap - 查询结果的封装规则)

通过< resultMap id="" type="" >映射,匹配列名与属性名。
<mapper namespace="com.qf.mybatis.part2.orm.ManagerDao">

    <!--定义resultMap标签-->
    <resultMap id="managerResultMap" type="com.qf.mybatis.part2.orm.Manager">
          <!--关联主键与列名-->
        <id property="id" column="mgr_id" />
          <!--关联属性与列名-->
        <result property="username" column="mgr_name" />
        <result property="password" column="mgr_pwd" />
    </resultMap>
  
     <!--使用resultMap作为ORM映射依据-->
    <select id="selectAllManagers" resultMap="managerResultMap">
        SELECT mgr_id , mgr_name , mgr_pwd
        FROM t_managers
    </select>
</mapper>

resultMap

十、MyBatis处理关联关系-多表连接【重点】

实体间的关系:关联关系(拥有 has、属于 belong)
OneToOne:一对一关系(Passenger--- Passport)
OneToMany:一对多关系(Department --- Employee )
ManyToMany:多对多关系(Student --- Subject)

Table建立外键关系

Entity添加关系属性

Mapper中将属性与列名对应

SQL语句

-- OneToOne 相关表
create table passenger(
    id int primary key auto_increment comment '主键ID',
    name varchar(20) not null comment '名字',
    sex varchar(1) not null default '男' comment '性别',
    birthday date comment '生日'
)default charset='utf8' comment '旅客表';

insert into passenger values (null,'杜猛','男',now());
insert into passport values (null,'中国',now(),1);
select * from passenger;
select * from passport;

create table passport(
    id int primary key auto_increment comment '主键ID',
    nationality varchar(20) not null comment '国家',
    expire date comment '过期时间',
    passenger_id int comment '旅客ID'
)default charset='utf8' comment '护照表';

select p1.id pid,p1.name name,p1.sex sex,p1.birthday birthday, p2.*
from passenger p1 
left join passport p2 
on p1.id = p2.passenger_id;

10.1 OneToOne

SQL参考OneToOneExample.sql
<?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.qf.hello.mybatis.dao.PassengerMapper">

    <!-- 结果映射(查询结果的封装规则) -->
    <resultMap id="passengerResultMap" type="passenger">
        <id column="pid" property="id"/>
        <result column="name" property="name"/>
        <result column="sex" property="sex"/>
        <result column="birthday" property="birthday"/>
        <!-- association:关联 property:属性名 javaType:关联的java对象的类型 -->
        <!-- 关系表中数据的封装规则 -->     <!-- 指定关系表的实体类型 -->
        <association property="passport" javaType="passport">
            <id column="id" property="id"/>
            <result column="nationality" property="nationality" />
            <result column="expire" property="expire"/>
        </association>
    </resultMap>

    <!-- 多表连接查询 -->                          <!-- 结果映射(查询结果的封装规则)-->
    <select id="selectById" resultType="passenger" resultMap="passengerResultMap">
        select p1.id pid, p1.name name, p1.sex sex, p1.birthday birthday, p2.*
        from passenger p1 left join passport p2 on p1.id = p2.passenger_id where p1.id=#{id}
    </select>
</mapper>
  • 注意:指定“一方”关系时(对象),使用< association javaType="" >

10.2 OneToMany

SQL参考OneToManyExample.sql
<resultMap id="deptResultMap" type="dept">
    <id property="id" column="did"/>
    <result property="name" column="name"/>
    <result property="location" column="location"/>
    <!-- 对象中的集合属性映射需要使用collection节点 property="属性名" ofType="集合中的元素类型" -->
    <collection property="emps" ofType="employee">
        <id property="empId" column="emp_id"/>
        <result property="empName" column="emp_name"/>
        <result property="salary" column="salary"/>
        <result property="deptId" column="dept_id"/>
    </collection>
</resultMap>

<select id="selectById" resultMap="deptResultMap" parameterType="int">
    select d.id did, d.name name, d.location location, e.*
    from dept d
    left join employee e
    on d.id = e.dept_id
    where d.id = #{id}
</select>
  • 注意:指定“多方”关系时(集合),使用< collection ofType="" >

映射图示

10.3 ManyToMany

SQL参考ManyToManyExample.sql

建立第三张关系表

多对多关系中,需要建立第三张关系表,当中存储两表中的外键,并设置为联合主键。

<!--封装多对多的结果-->
<resultMap id="studentResultMap" type="student">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="sex" column="sex"/>
    <!-- property="集合属性的名称" ofType="集合里对象的类型" -->
    <collection property="subjectList" ofType="subject">
        <id property="id" column="sid"/>
        <result column="sname" property="name"/>
        <result column="grade" property="grade"/>
    </collection>
</resultMap>

<!--根据ID查询学生-->
<select id="selectById" resultMap="studentResultMap" parameterType="int">
    select s1.*,s2.id sid,s2.name sname,s2.grade grade
    from student s1
    left join student_subject ss
    on s1.id = ss.student_id
    left join subject s2
    on s2.id=ss.subject_id
    where s1.id=#{id}
</select>
  • 注意:指定“多方”关系时(集合),使用< collection ofType="" >

10.4 关系总结

一方,添加集合;多方,添加对象。
双方均可建立关系属性,建立关系属性后,对应的Mapper文件中需使用< ResultMap >完成多表映射。
持有对象关系属性,使用< association property="dept" javaType="department" >
持有集合关系属性,使用< collection property="emps" ofType="employee" >

十一、动态SQL【重点】

MyBatis的映射文件中支持在基础SQL上添加一些逻辑操作,并动态拼接成完整的SQL之后再执行,以达到SQL复用、简化编程的效果。

使用UserMapper举例子

11.1 < sql >

<!-- 定义SQL片段 -->
<sql id="allField">
    select *
    from users
</sql>

<select id="selectUserByName" resultType="user" parameterType="string">
    <include refid="allField"/>
    where name like concat('%', #{name}, '%')
</select>

11.2 < if >

<select id="selectUserByNameOrPassword" resultType="User">
    select * from users
    <where>
        <if test="name != null and name !=''">
            or name =#{name}
        </if>
        <if test="pwd != null and pwd !=''">
            or password =#{pwd}
        </if>
    </where>
</select>

11.3 < where >

<select id="selectUserByNameOrPassword" resultType="User">
    select * from users
    <where>
        <if test="name != null and name !=''">
            or name =#{name}
        </if>
        <if test="pwd != null and pwd !=''">
            or password =#{pwd}
        </if>
    </where>
</select>

where标签可以过滤的前缀:

11.4 < set >

动态更新

<update id="updateUserDymatic" parameterType="user">
    update users
    <set>
        <if test="name !=null and name!=''">name=#{name},</if>
        <if test="password !=null and password!=''">password=#{password},</if>
        <if test="sex !=null and sex!=''">sex=#{sex},</if>
        <if test="birthday !=null">birthday=#{birthday},</if>
        <if test="registTime !=null">registTime=#{registTime},</if>
    </set>
    where id = #{id}
</update>

11.5 < trim >

动态添加和去除前后缀

< trim prefix="" suffix="" prefixOverrides="" suffixOverrides="" >代替< where > 、< set >
prefix前缀 -[prefixOverrides前缀去重] +内容 -[suffixOverrides后缀去重] + suffix后缀

prefixOverrides/suffixOverrides 如果有多个值的情况下,使用|分割

<select id="selectUserByNameOrPassword" resultType="User">
    select * from users
    <trim prefix="WHERE" prefixOverrides="and|or">
        <if test="name != null and name !=''">
            or name =#{name}
        </if>
        <if test="pwd != null and pwd !=''">
            or password =#{pwd}
        </if>
    </trim>
</select>
<update id="updateUserDynamic" parameterType="user">
    update users
    <trim prefix="set" suffixOverrides=",">
        <if test="name !=null and name!=''">name=#{name},</if>
        <if test="password !=null and password!=''">password=#{password},</if>
        <if test="sex !=null and sex!=''">sex=#{sex},</if>
        <if test="birthday !=null">birthday=#{birthday},</if>
        <if test="registTime !=null">registTime=#{registTime},</if>
    </trim>
    where id = #{id}
</update>

11.6 < foreach >

批量删除
<!--  delete from users where id in (2,3,4)  -->
<!-- 如果传入的参数是一个集合 foreach的collection固定写list -->
<delete id="deleteByIdList" parameterType="list">
    delete from users where id in
    <foreach collection="list" open="(" close=")" item="uid" separator=",">
        #{uid}
    </foreach>
</delete>

<!-- 如果传入的参数是一个数组 foreach的collection固定写array -->
<delete id="deleteByIdArray" parameterType="int[]">
    delete from users where id in
    <foreach collection="array" open="(" close=")" item="uid" separator=",">
        #{uid}
    </foreach>
</delete>



-----批量添加
<insert id="addUsers">
    insert into users values
    <foreach collection="list" item="user"  separator=",">
        (null,#{user.name},#{user.password},#{user.sex},#{user.birthday})
    </foreach>
</insert>

参数

描述

取值

collection

容器类型

list、array、map

open

起始符

(

close

结束符

)

separator

分隔符

,

index

下标号

从0开始,依次递增

item

当前项

任意名称(循环中通过 #{任意名称} 表达式访问)

十二、缓存(Cache)【重点】

内存中的一块存储空间,服务于某个应用程序,旨在将频繁读取的数据临时保存在内存中,便于二次快速访问。

无缓存:用户在访问相同数据时,需要发起多次对数据库的直接访问,导致产生大量IO、读写硬盘的操作,效率低下

有缓存:首次访问时,查询数据库,将数据存储到缓存中;再次访问时,直接访问缓存,减少IO、硬盘读写次数、提高效率

12.1 一级缓存

SqlSession级别的缓存,同一个SqlSession的发起多次同构查询,会将数据保存在一级缓存中。
  • 注意:无需任何配置,默认开启一级缓存。

在sqlsession 中有一个数据结构 是map 结构, 这个区域就是一级缓存区域,一级缓存区域中的 key 是由 sql 语句 方法参数 statement 等 组成的一个唯一值 对应的 value 就是 缓存内容 , 一级缓存 map 的生命周期 和当前 sqlSession 一致

12.2 二级缓存

SqlSessionFactory级别的缓存,同一个SqlSessionFactory构建的SqlSession发起的多次同构查询,会将数据保存在二级缓存中。
  • 注意:在sqlSession.commit()或者sqlSession.close()之后生效。

二级缓存指的是 同一个 namespace 下的 mapper二级缓存的数据结构 也是一个 map 二级缓存 需要 手动开启

12.2.1 开启全局缓存

< settings >是MyBatis中极为重要的调整设置,他们会改变MyBatis的运行行为,其他详细配置可参考官方文档。
<configuration>
    <properties .../>
      
      <!-- 注意书写位置 -->
    <settings>
        <setting name="cacheEnabled" value="true"/> <!-- mybaits-config.xml中开启全局缓存(默认开启) -->
    </settings>
  
      <typeAliases></typeAliases>
</configuration>

12.2.2 指定Mapper缓存

<mapper namespace="com.qf.mybatis.part2.cache.BookDao">
    <cache /> <!-- 开启 二级缓存   必须指定 这个   标签 -->

    <select id="selectBookByCondition" resultType="com.qf.mybatis.part2.cache.Book">
        SELECT * FROM t_books
    </select>
</mapper>
@Test
public void testMapperCache(){

      SqlSession sqlSession1 = MyBatisUtils.getSession();
  
      BookDao bookDao1 = sqlSession1.getMapper(BookDao.class);

      bookDao1.selectBookByCondition(new Book());

      sqlSession1.close(); //必须关闭SqlSession才可缓存数据

      //--------------------

      SqlSession sqlSession2 = MyBatisUtils.getSession();

      BookDao bookDao2 = sqlSession2.getMapper(BookDao.class);

      bookDao2.selectBookByCondition(new Book());

      sqlSession2.close(); //缓存击中
}

12.2.3 缓存清空并重新缓存

@Test
public void testMapperCache(){

      SqlSession sqlSession1 = MyBatisUtils.getSession();
  
      BookDao bookDao1 = sqlSession1.getMapper(BookDao.class);

      bookDao1.selectBookByCondition(new Book());

      sqlSession1.close(); //必须关闭SqlSession才可缓存数据到二级缓存

      //--------------------
      
        SqlSession sqlSession3 = MyBatisUtils.getSession();

        BookDao bookDao3 = sqlSession3.getMapper(BookDao.class);

        bookDao3.deleteBookById(102);

        sqlSession3.commit(); //DML成功,数据发生变化,缓存清空

        sqlSession3.close();
  
      //--------------------

      SqlSession sqlSession2 = MyBatisUtils.getSession();

      BookDao bookDao2 = sqlSession2.getMapper(BookDao.class);

      bookDao2.selectBookByCondition(new Book());

      sqlSession2.close(); //缓存未击中,重新查询数据库、重新缓存
}

十三、Druid连接池

13.1 概念

Druid 是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池、插件框架和 SQL 解析器组成。该项目主要是为了扩展 JDBC 的一些限制,可以让程序员实现一些特殊的需求,比如向密钥服务请求凭证、统计 SQL 信息、SQL 性能收集、SQL 注入检查、SQL 翻译等,程序员可以通过定制来实现自己需要的功能。

13.2 不同连接池对比

测试执行申请归还连接 1,000,000(一百万)次总耗时性能对比。

13.2.1 测试环境

环境

版本

OS

OS X 10.8.2

CPU

Intel i7 2GHz 4 Core

JVM

Java Version 1.7.0_05

13.2.2 基准测试结果对比

JDBC-Conn Pool

1 Thread

2 threads

5 threads

10 threads

20 threads

50 threads

Druid

898

1,191

1,324

1,362

1,325

1,459

tomcat-jdbc

1,269

1,378

2,029

2,103

1,879

2,025

DBCP

2,324

5,055

5,446

5,471

5,524

5,415

BoneCP

3,738

3,150

3,194

5,681

11,018

23,125

jboss-datasource

4,377

2,988

3,680

3,980

32,708

37,742

C3P0

10,841

13,637

10,682

11,055

14,497

20,351

Proxool

16,337

16,187

18,310(Ex)

25,945

33,706(Ex)

39,501 (Ex)

13.2.3 测试结论

  • Druid 是性能最好的数据库连接池,tomcat-jdbc 和 druid 性能接近。

  • Proxool 在激烈并发时会抛异常,不适用。

  • C3P0 和 Proxool 都相当慢,影响 sql 执行效率。

  • BoneCP 性能并不优越,采用 LinkedTransferQueue 并没有能够获得性能提升。

  • 除了 bonecp,其他的在 JDK 7 上跑得比 JDK 6 上快。

  • jboss-datasource 虽然稳定,但性能很糟糕。

13.3 配置pom.xml

引入Druid依赖
<<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>

13.4 创建DruidDataSourceFactory

MyDruidDataSourceFactory并继承PooledDataSourceFactory,并替换数据源。
package com.qf.mybatis.part2.utils;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;

public class MyDruidDataSourceFactory extends PooledDataSourceFactory {
    public MyDruidDataSourceFactory() {
        this.dataSource = new DruidDataSource();//替换数据源
    }
}

13.5 修改mybatis-config.xml

mybatis-config.xml中连接池相关配置。
<!--连接池     注意这里的属性名 -->
<dataSource type="com.qf.mybatis.part2.utils.DruidDataSourceFactory"><!--数据源工厂-->
    <property name="driverClass" value="${driver}"/>
    <property name="jdbcUrl" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/> 
  
</dataSource>

注意:< property name="属性名" />属性名必须与com.alibaba.druid.pool.DruidAbstractDataSource中一致。

十四、PageHelper

14.1 概念

PageHelper是适用于MyBatis框架的一个分页插件,使用方式极为便捷,支持任何复杂的单表、多表分页查询操作。

14.2 访问与下载

官方网站: https://pagehelper.github.io/
下载地址: https://github.com/pagehelper/Mybatis-PageHelper

14.3 开发步骤

PageHelper中提供了多个分页操作的静态方法入口。

14.3.1 引入依赖

pom.xml中引入PageHelper依赖。
<dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.1.10</version>
</dependency>

14.3.2 配置MyBatis-config.xml

在MyBatis-config.xml中添加< plugins >。
<configuration>
      <typeAliases></typeAliases>
  
    <plugins>
        <!-- com.github.pagehelper为PageHelper类所在包名 -->
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
  
      <environments>...</environments>
</configuration>

14.3.3 PageHelper应用方式

使用PageHelper提供的静态方法设置分页查询条件。
@Test
public void testPagehelper(){
        UserDao userDao = MyBatisUtils.getMapper(UserDao.class);
        PageHelper.startPage(1,2);//使用PageHelper设置分页条件
        List<User> users = userDao.selectAllUsers();
        for(User user : users){
                System.out.println(user);
        }
}

14.4 PageInfo对象

PageInfo对象中包含了分页操作中的所有相关数据。

PageInfo结构图

14.4.1 PageInfo应用方式

使用PageInfo保存分页查询结果。
@TestpublicvoidtestPageInfo(){
		UserDao userDao =MyBatisUtils.getMapper(UserDao.class);
		PageHelper.startPage(6,2);
		List<User> users = userDao.selectAllUsers();
		PageInfo<User> pageInfo =newPageInfo<User>(users);//将分页查询的结果集保存在PageInfo对象中
		System.out.println(pageInfo);}

14.4.2 注意事项

只有在PageHelper.startPage()方法之后的 第一个查询会有执行分页
分页插件 不支持带有“for update”的查询语句。
分页插件不支持 “嵌套查询”,由于嵌套结果方式会导致结果集被折叠,所以无法保证分页结果数量正确。。

14.4.3 分页练习

使用Servlet+JSP+MyBatis+分页插件,完成分页查询功能。

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

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

相关文章

《图机器学习》-Node Embeddings

Node Embeddings一、Graph Representation Learning二、Node Embeddings&#xff1a;Encoder and Decoder三、Random Walk Approaches for Node Embeddings一、Graph Representation Learning 在传统的图机器学习中&#xff0c;依赖于手工特征工程(即由特征工程师去设计节点、…

每日一题:Leetcode54. 螺旋矩阵

文章目录 系列&#xff1a;数组专练 语言&#xff1a;java & go 题目来源&#xff1a;Leetcode54. 螺旋矩阵 难度&#xff1a;中等 考点&#xff1a;边界问题处理 思路和参考答案文章目录题目描述思路java参考代码go参考代码&#xff1a;题目描述 给你一个 m 行 n 列的矩…

力扣sql基础篇(三)

力扣sql基础篇(三) 1 查询结果的质量和占比 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # 把该列置为null的话,它就不会统计了 SELECT query_name,ROUND(sum(rating/position)/count(*),2) quality, ROUND(count(IF(rating<3,rating,null))/c…

【数据结构】—— Java实现双向链表

Java实现双向链表一、链表的概念及结构二、头指针与头结点的异同三、代码实现一、链表的概念及结构 我们在单链表中&#xff0c;有了next指针。这就使得我们查找下一个结点的时间复杂度为O(1)。可是我们要查找的是上一个结点的话&#xff0c;那最坏的时间复杂度就是O(n)了&…

用图记忆C语言中的运算符优先级

运算符优先级以及结合方向的统计表&#xff0c;网上到处可见。本文画了一张图&#xff0c;以便记忆&#xff01; 1. 总体来说优先级 初级运算 > 单目运算 > 双目运算 > 三目运算 > 赋值运算 > 逗号运算 2. 双目细分 算术运算 > 位移运算 > 关系运算 &g…

今年十八,基础过关

目录 前言 第一关&#xff1a;KEY在哪里 再加密一次你就得到key啦~ 猜猜这是经过了多少次加密&#xff1f; 据说MD5加密很安全&#xff0c;真的是么&#xff1f; 种族歧视 HAHA浏览器 key究竟在哪里呢&#xff1f; key又找不到了 冒充登陆用户​编辑 比较数字大小 就不让你…

Linux应用编程---14.UDP服务器、客户端编程

Linux应用编程—14.UDP服务器、客户端编程 ​ 之前有介绍过UDP是一种无连接、尽最大努力交付、面向报文的协议。应用层交给UDP多长的报文&#xff0c;UDP就照样发送。Linux下UDP属于数据报socket。数据报socket流程图如图1所示&#xff1a; 图1 数据报socket流程图 ​ 新引入的…

FourIE:一种最新联合事件抽取SOTA论文解读

Cross-Task Instance Representation Interactions and Label Dependencies for Joint Information Extraction with Graph Convolutional Networks 论文&#xff1a;Cross-Task Instance Representation Interactions and Label Dependencies for Joint Information Extractio…

Vue2的前端路由(vue-router)

一、路由 路由 (英文&#xff1a;router)就是页面地址与组件之间的对应关系 二、路由方式 服务器端路由、前端路由 三、前端路由 前端路由&#xff1a;地址和组件之间的对应关系&#xff0c;即由前端来维护一组路由规则&#xff08;地址和组件之间的对应关系&#xff09;&…

xlsx.utils.sheet_to_json的{ header: 1 }起的作用,header属性的研究

XLSX.utils.sheet_to_json 是为了把excel里面的数据解析出来&#xff0c;这是它的定义&#xff1a; 其中&#xff0c;worksheet表示特定表名的工作表&#xff0c;opts属于可要可不要的参数。 opts也有几种类型&#xff1a; export interface Sheet2JSONOpts extends DateNFOp…

selinux 控制

在某些Linux发行版上&#xff0c;默认情况下启用SELinux&#xff0c;如果不了解SELinux的工作原理以及如何配置它的基本详细信息&#xff0c;则可能会导致一些不必要的问题。一般强烈建议了解了SELinux 之后再去实现它。但是&#xff0c;在了解 SELinux 的实现细节之前&#xf…

由浅入深,详解ViewModel的那些事

Hi&#xff0c;你好 &#x1f603; 引言 关于 ViewModel &#xff0c;Android 开发的小伙伴应该都非常熟悉&#xff0c;无论是新项目还是老项目&#xff0c;基本都会使用到。而 ViewModel 作为 JetPack 核心组件&#xff0c;其本身也更是承担着不可或缺的作用。 因此&#x…

STL list 模拟实现

list 概述 相比于 vector 的连续线性空间&#xff0c;list 采用的是零散的空间&#xff0c;它的好处是每次插入或删除一个元素&#xff0c;就配置或释放一个元素空间。 list 是支持常数时间从容器任何位置插入和移除元素容器&#xff0c;但不支持快速随机访问。list 通常实现…

Linux操作系统之进程间通讯—共享内存与消息队列

文章目录一、共享内存1、共享内存的原理2、共享内存的实现三、消息队列1、消息队列原理2、消息队列实现一、共享内存 1、共享内存的原理 共享内存为多个进程之间共享和传递数据提供了一种有效的方式。共享内存是先在物理内存上申请一块空间&#xff0c;多个进程可以将其映射到…

dp (四) 打家劫舍

打家劫舍(一)_牛客题霸_牛客网 描述 你是一个经验丰富的小偷&#xff0c;准备偷沿街的一排房间&#xff0c;每个房间都存有一定的现金&#xff0c;为了防止被发现&#xff0c;你不能偷相邻的两家&#xff0c;即&#xff0c;如果偷了第一家&#xff0c;就不能再偷第二家&#…

离线和实时

离线和实时 一、数仓基本概念 1. 数据仓库架构 我们在谈数仓之前&#xff0c;为了让大家有直观的认识&#xff0c;先来谈数仓架构&#xff0c;“架构”是什么&#xff1f;这个问题从来就没有一个准确的答案。这里我们引用一段话&#xff1a;在软件行业&#xff0c;一种被普遍…

8种将pdf转化成excel的方法,亲测实用又有效!

PDF 到 Excel 的在线或离线转换工具可帮助您将原始或扫描的 PDF 文件转换为 Excel 格式。将 PDF 转换为 Excel 主要是为了获得可编辑的 Excel 文件或满足其他目标&#xff1b; 通过消除容易出错的手动复制粘贴来保持数据准确性。在需要使用 Excel 格式的大量 PDF 数据时节省时…

药物临床试验数据分析(靶点|适应症|企业|登记信息)

临床试验相关工作者在对药物进行系统性研究时都需要对药物做临床试验&#xff0c;且在对药物进行临床试验前和临床试验期间都需要对相关药物临床试验数据信息进行全面的分析及了解&#xff0c;有助于目标药物临床试验的顺利开展。药物临床试验数据覆盖非常宽泛&#xff0c;包含…

【进阶C语言】自定义类型——结构体+枚举+联合体

文章目录一.结构体1.内存对齐存在的原因规则举例2.位段二.枚举定义枚举的优点三.联合体定义特点内存计算一.结构体 1.内存对齐 存在的原因 平台原因(移植原因)&#xff1a; 不是所有的硬件平台都能访问任意地址上的任意数据的&#xff1b;某些硬件平台只能在某些地址处取某些…

【PHPWord】使用PHPWord自动生成TOC根据内容的目录完整示例 | table of contents (TOC)

目录 一、什么是Word中的目录二、目录的生成在Word中是如何操作的三、PHPWord中目录的生成1. 插入目录2.添加页码3.修改目录的字体样式4.修改目录的目录样式5.修改生成目录的标题等级四、完整示例代码和效果图一、什么是Word中的目录 在我们日常使用中,经常需要在文档中插入目…