Java之MyBatis

MyBatis框架实战:优化SQL映射与动态查询

Java之MyBatis

ORM介绍

在这里插入图片描述

  • 对象:Java实体对象
  • 关系:关系型数据库
  • 映射:二者之间的对应关系
Java概念 数据库概念
属性 字段
对象 记录/行

背景

原始JDBC操作问题分析

1、频繁创建和销毁数据库的连接会造成系统的资源浪费从而影响系统性能;

2、sql硬编码,如需修改,则需改动java代码,不易维护;

3、查询操作,结果集与实体需要手动映射,较为繁琐;

4、增删改操作涉及到的参数需要,手动设置并处理占位符问题。

原始JDBC问题解决方案

1、使用数据库连接池初始化连接资源;

2、SQL语句抽取到配置文件中;

3、通过反射、内省等底层技术,将实体与字段自动映射。

MyBatis框架

介绍

  • MyBatis是一个优秀的基于Java的持久层框架,它内部封装了JDBC,使开发者只需要关注SQL语句本身,而不需要花费精力去处理加载驱动、创建连接、创建执行者对象等复杂繁琐的操作;
  • MyBatis通过xml或注解的方式将要执行的的各种Statement配置起来,通过Java对象和Statement中的SQL动态参数进行映射生成最终要执行的SQL语句;
  • 最后MyBatis框架执行完SQL并将结果映射为Java对象并进行返回,通过ORM思想解决实体与数据库字段映射问题,对JDBC封装,屏蔽了JDBC原生的API底层访问细节,避免直接和底层的JDBC API打交道;

快速入门

项目结构

在这里插入图片描述

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.test</groupId>
    <artifactId>MyBatisDemo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--MyBatis核心-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <!--MySQL驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
        <!--日志-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>

</project>
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核心根标签-->
<configuration>
    <properties resource="jdbc.properties"/>
    <!--数据库连接环境,可通过environment子标签来配置多个环境,default来指定使用的是哪个环境-->
    <environments default="development">
        <!--id唯一标识-->
        <environment id="development">
            <!--事务管理,采用JDBC默认事务-->
            <transactionManager type="JDBC"/>
            <!--数据源,数据库连接池-->
            <dataSource type="POOLED">
                <!-- 连接数据库四要素 -->
                <property name="driver" value="${jdbc.driverClass}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 加载映射配置文件 -->
    <mappers>
        <!--扫描指定包下的mapper.xml文件-->
        <!--<package name="com.test.mapper"/>-->
        <!--指定配置文件的名称-->
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>
</configuration>
jdbc.properties(数据库连接配置)
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root
实体类
package com.test.pojo;

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

import java.io.Serializable;

/**
 * 映射数据库中的t_user表
 *
 * @author zhangzengxiu
 * @date 2022/9/3
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User implements Serializable {
   
   
    private static final long serialVersionUID = -2427372682522927866L;

    /**
     * 主键id
     */
    private Integer id;

    /**
     * 用户名
     */
    private String usetname;

    /**
     * 密码
     */
    private String password;

    /**
     * 年龄
     */
    private Integer age;

    /**
     * 性别
     */
    private String sex;

    /**
     * 邮箱
     */
    private String email;

}
数据库表
CREATE TABLE `t_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `username` varchar(20) DEFAULT NULL COMMENT '用户名',
  `password` varchar(30) DEFAULT NULL COMMENT '密码',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `sex` varchar(2) DEFAULT NULL COMMENT '性别',
  `email` varchar(30) DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
mapper接口
package com.test.mapper;

import com.test.pojo.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author zhangzengxiu
 * @date 2022/9/3
 */
public interface UserMapper {
   
   

    /**
     * 新增user
     *
     * @param user
     * @return
     */
    int insertUser(User user);

    /**
     * 根据id删除user
     *
     * @param id
     * @return
     */
    int delUserById(@Param("id") Integer id);

    /**
     * 更新user
     *
     * @param user
     * @return
     */
    int updateUser(User user);

    /**
     * 查询user
     *
     * @param id
     * @return
     */
    User queryUser(@Param("id") Integer id);

    /**
     * 查询user列表
     *
     * @return
     */
    List<User> queryUserList();

}

mapper.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接口对应-->
<mapper namespace="com.test.mapper.UserMapper">

    <!--添加user-->
    <insert id="insertUser" parameterType="com.test.pojo.User">
      INSERT INTO t_user
         (
         `username`,
         password,
         age,
         sex,
         email
        )
      VALUES
        (
        #{username},
        #{password},
        #{age},
        #{sex},
        #{email}
        )
    </insert>

    <!--根据id删除user-->
    <delete id="delUserById">
      DELETE FROM t_user
      WHERE id=#{id}
    </delete>

    <!--更新user-->
    <update id="updateUser">
      UPDATE t_user
      SET username=#{username},
      password=#{password},
      age= #{age},
      sex=#{sex},
      email=#{email}
      WHERE id=#{id}
    </update>

    <!--查询user-->
    <select id="queryUser" resultType="com.test.pojo.User">
      SELECT
        id,
        username,
        password,
        age,
        sex,
        email
      FROM
        t_user
      WHERE
        id=#{id}
    </select>

    <!--查询user列表-->
    <select id="queryUserList" resultType="com.test.pojo.User">
    SELECT
        id,
        username,
        password,
        age,
        sex,
        email
    FROM t_user
  </select>

</mapper>

mapper接口全类名—>mapper映射配置文件.xml—>SQL语句—>ORM映射

1、映射文件的namespace与mapper接口全类名保持一致

2、映射文件中的SQL语句的id与mapper接口方法名保持一致

3、查询功能标签必须设置resultType或resultMap

3.1resultType:设置默认的映射关系,java属性值与数据库字段列名完全匹配

3.2resultMap:设置自定义的映射关系,java属性与数据库字段列名不匹配情况或联合查询

单测
工具类
package com.test.util;

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;

/**
 * Sqlsession工具类
 *
 * @author zhangzengxiu
 * @date 2022/9/3
 */
public class SqlSessionUtil {
   
   

    /**
     * 获取Sqlsession
     *
     * @return
     */
    public static SqlSession getSqlSession() throws Exception {
   
   
        //加载核心配置文件
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        //获取SqlSessionFactoryBuilder
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //获取SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
        //获取SqlSession 设置为自动提交
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        return sqlSession;
    }

}

测试代码
package com.test.mapper;

import com.test.pojo.User;
import com.test.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Assert;
import org.junit.Test;

import java.util.List;

/**
 * @author zhangzengxiu
 * @date 2022/9/3
 */
public class UserMapperTest {
   
   

    /**
     * 测试添加user方法
     *
     * @throws Exception
     */
    @Test
    public void testInsertUser() throws Exception {
   
   
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        /*
            SqlSession底层通过代理模式
            为mapper接口创建mapper接口的实现类对象并返回
         */
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = new User(null, "zhanghsan", "123454", 23, "男", "1234@qq.com");
        int res = userMapper.insertUser(user);
        Assert.assertEquals(1, res);
    }

    /**
     * 测试删除user方法
     *
     * @throws Exception
     */
    @Test
    public void testDelUser() throws Exception {
   
   
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        int res = userMapper.delUserById(20);
        Assert.assertEquals(1, res);
    }

    /**
     * 测试更新user方法
     *
     * @throws Exception
     */
    @Test
    public void testUpdateUser() throws Exception {
   
   
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setId(22);
        user.setUsername("yyccf");
        user.setPassword("222");
        user.setAge(30);
        user.setSex("男");
        user.setEmail("1222@123.com");
        int res = userMapper.updateUser(user);
        Assert.assertEquals(1, res);
    }

    /**
     * 测试查询user方法
     *
     * @throws Exception
     */
    @Test
    public void testQueryUser() throws Exception {
   
   
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = userMapper.queryUser(23);
        System.out.println("user = " + user);
    }

    /**
     * 测试查询user列表方法
     *
     * @throws Exception
     */
    @Test
    public void testQueryUserList() throws Exception {
   
   
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = userMapper.queryUserList();
        for (User user : users) {
   
   
            System.out.println(user);
        }
    }

}

注意:

踩坑:

由于我们核心配置文件中配置的事务管理是JDBC,

则我们的事务都需要手动去处理,需要手动提交事务才会生效

sqlSession.commit();

或者创建sqlSession对象时,设置为自动提交,即可不用手动提交

//获取SqlSession 设置为true自动提交
SqlSession sqlSession = sqlSessionFactory.openSession(true);

小技巧:

想查看实际执行的SQL语句:

通过debug打断点到org.apache.ibatis.executor.SimpleExecutor类

  • 增删改类方法:debug断点到doUpdate()
  • 查询方法:debug断点doQuery

补充:

SqlSession:Java程序与数据库之间的会话;由MyBatis为我们提供的操作数据库的会话对象

SqlSessionFactory:生产sqlSession工厂;

优化
事务管理

设置事务为自动提交,不手动控制事务

JDBC事务默认自动提交,可以通过手动事务管理
connection.setAutoCommit(false);
MyBatis采用JDBC事务管理器,默认所有事物的提交或回滚都需要手动处理

JDBC事务默认自动提交:

在这里插入图片描述

MyBatis采用JDBC事务管理器,sqlSession默认不自动提交:
在这里插入图片描述

核心代码
//事务管理
//获取SqlSession 默认为false,需要手动控制事务,提交事务等
SqlSession sqlSession = sqlSessionFactory.openSession();
//手动提交事务
sqlSession.commit();

//获取SqlSession 设置为自动提交
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//提交事务(不需要了)
//sqlSession.commit();
MyBatis日志记录
核心操作

pom依赖

<!--日志-->
<dependency>
   	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
	<version>1.2.17</version>
</dependency>

日志文件 log4j.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Encoding" value="UTF-8"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L)\n"/>
        </layout>
    </appender>
    <logger name="java.sql">
        <level value="debug"/>
    </logger>
    <logger name="org.apache.ibatis">
        <level value="info"/>
    </logger>
    <root>
        <level value="debug"/>
        <appender-ref ref="STDOUT"/>
    </root>
</log4j:configuration>

控制台打印信息
在这里插入图片描述

日志级别

FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)

从左到右内容越详细

核心配置文件

SSM(Spring+SpringMVC+MyBatis)框架该配置文件可以省略

mybatis-confuig.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核心根标签-->
<configuration>
    <!--引入properties文件-->
    <properties resource="jdbc.properties"/>

    <!--类型别名,
        ype:设置别名的类型   (按照类型设置不常用,一般是指定包)
        alias可选:设置某个类型的别名。
            不设置默认是类名且不区分大小写;设置了则为设置的别名且不区分大小写
    -->
    <typeAliases>
        <!--alias可以指定固定的别名(不区分大小写)不常用!!!-->
        <!--<typeAlias type="com.test.pojo.User" alias="User"/>-->
        <!--不加alias,则别名默认为类名不区分大小写 不常用!!!-->
        <!--<typeAlias type="com.test.pojo.User"/>-->
        <!--设置指定包下所有的类默认类型别名(类名,不区分大小写) 常用!!!-->
        <package name="com.test.pojo"/>
    </typeAliases>

    <!--
    environments:配置多个连接数据库的环境,可通过environment子标签来配置多个环境
    属性:
        default:设置默认使用的环境
    -->
    <environments default="development">
        <!--id唯一标识,不能重复-->
        <environment id="development">
            <!--事务管理,采用JDBC默认事务
            type:
                JDBC:表示当前环境中,执行SQL时,使用的是JDBC原生的事务管理方式,事务的提交、回滚等操作需要手动处理
                MANAGED:被管理,例如Spring。Spring与MyBatis整合后,可通过Spring中的声明式事务的方式来进行管理
            -->
            <transactionManager type="JDBC"/>
            <!--数据源,数据库连接池-->
            <!--
            type:设置数据源类型
                POOLED: 使用数据库连接池,缓存数据库库的连接,下次可直接从缓存中进行获取
                UNPOOLED:不是用数据库连接池
                JNDI:使用上下文中的数据源,例如Spring管理数据源。MyBatis来使用数据源。
                    Spring整合MyBatis后就不需要提供数据源了,Spring本身提供的就有数据源
            -->
            <dataSource type="POOLED">
                <!-- 连接数据库四要素 -->
                <property name="driver" value="${jdbc.driverClass}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 加载映射配置文件 -->
    <mappers>
        <!--扫描指定包下的mapper.xml文件-->
        <!--
            以包为单位引入映射配置文件(推荐!!!)
            注意:
                1、Mapper接口包所在的目录与映射文件所在的目录保持一致
                2、Mapper接口要与映射文件名称保持一致 UserMapper<->UserMapper.xml
            以xml文件的形式引入:
                如果xml文件很多会非常麻烦,还容易遗漏
        -->
        <!--指定xml映射文件目录(推荐)-->
        <package name="com.test.mapper"/>
        <!--指定配置文件的名称(不推荐)-->
        <!--<mapper resource="mappers/UserMapper.xml"/>-->
    </mappers>
</configuration>
typeAliases类型别名

查询返回的实体resultType是全限定类名,较为繁琐,可通过起别名的方式进行优化

设置别名前:

<!--查询user-->
    <select id="queryUser" resultType="com.test.pojo.User">
      SELECT
        id,
        username,
        password,
        age,
        sex,
        email
      FROM
        t_user
      WHERE
        id=#{id}
    </select>

设置类名别名:

注意配置顺序!!!

<!--类型别名,
        ype:设置别名的类型   (按照类型设置不常用,一般是指定包)
        alias可选:设置某个类型的别名。
            不设置默认是类名且不区分大小写;设置了则为设置的别名且不区分大小写
    -->
    <typeAliases>
        <!--alias可以指定固定的别名(不区分大小写)不常用!!!-->
        <!--<typeAlias type="com.test.pojo.User" alias="User"/>-->
        <!--不加alias,则别名默认为类名不区分大小写 不常用!!!-->
        <!--<typeAlias type="com.test.pojo.User"/>-->
        <!--设置指定包下所有的类默认类型别名(类名,不区分大小写) 常用!!!-->
        <package name="com.test.pojo"/>
    </typeAliases>

设置类型别名后:

<!--查询user-->
    <select id="queryUser" resultType="user">
      SELECT
        id,
        username,
        password,
        age,
        sex,
        email
      FROM
        t_user
      WHERE
        id=#{id}
    </select>
mappers引入映射文件

与接口对应的映射配置文件必须引入到核心配置文件中

  <!-- 加载映射配置文件 -->
    <mappers>
        <!--扫描指定包下的mapper.xml文件-->
        <!--
            以包为单位引入映射配置文件(推荐!!!)
            注意:
                1、Mapper接口包所在的目录与映射文件所在的目录保持一致
                2、Mapper接口要与映射文件名称保持一致 UserMapper<->UserMapper.xml
            以xml文件的形式引入:
                如果xml文件很多会非常麻烦,还容易遗漏
        -->
        <!--指定xml映射文件目录(推荐)-->
        <package name="com.test.mapper"/>
        <!--指定配置文件的名称(不推荐)-->
        <!--<mapper resource="mappers/UserMapper.xml"/>-->
    </mappers>

MyBatis获取参数值

mapper接口中参数的值在mapper.xml映射配置文件中如何获取

${} 与#{}

${}本质就是字符串拼接,可能会造成SQL注入,单引号需要手动处理

#{}本质就是占位符赋值

MyBatis获取参数值的不同情况:
情况一:

Mapper接口方法的参数为单个的字面量类型

Mapper接口

	/**
     * 根据username查询User
     *
     * @param username
     * @return
     */
    User getUserByUsername(String username);

映射配置文件

	<!--根据username查询user-->
    <select id="getUserByUsername" resultType="user">
        <!--可查询-->
        <!--SELECT * FROM t_user WHERE username = #{username}-->
        <!--可查询-->
        <!--SELECT * FROM t_user WHERE username = #{aaa}-->
        <!--可查询,需要手动拼接单引号-->
        <!--SELECT * FROM t_user WHERE username ='${username}'-->
        <!--可查询,需要手动拼接单引号-->
        SELECT * FROM t_user WHERE username = '${aaa}'
    </select>

#{}:#{username}与 #{aaa}均可查询

占位符赋值与值没关系,只与位置有关

#{}获取,可以以任意名称来获取。本质就是占位符
在这里插入图片描述

:{}:{username}与 ${aaa}均可查询。本质就是字符串拼接

${}获取,可以以任意名称来获取,但是如果是字符串类型,需要手动拼接单引号否则报错
在这里插入图片描述

报错信息:

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值