Mybatis持久层框架 面向官方文档学习的一片博文

Mybatis

1.简介


1.1什么是Mybatis

MyBatis logo
  • MyBatis 是一款优秀的持久层框架.

  • 它支持自定义 SQL、存储过程以及高级映射。

  • MyBatis免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。

  • MyBatis可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

  • MyBatis 本是apache的一个开源项目iBatis,.

  • 2010年这个项目由apache software foundation 迁移到了[google code](https://baike.baidu.com/item/google code/2346604),并且改名为MyBatis 。

  • 2013年11月迁移到Github

1.2如何获得Mybatis

Maven仓库:

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
</dependency>

GitHub:https://github.com/mybatis/mybatis-3/releases

中文文档:https://mybatis.org/mybatis-3/zh/index.html

1.3持久化

数据持久化

  • 持久化就是将程序的数据在持久状态和瞬时状态转换的过程
  • 内存:断电即失
  • 数据库(JDBC)IO文件持久化

1.4数据持久层(Dao)

Dao,Service,controller

界限明显

1.5为什么需要Mybatis

  • 简化JDBC操作 框架实现
  • 帮助程序员将数据存到数据库中
  • 使用人多 框架主流

2.第一个Mybatis程序

思路:搭建环境–>导入Mybatis–>编写程序–>测试

2.1搭建环境

搭建数据库

CREATE DATABASE `mybatis`;

`mybatis`

CREATE TABLE `user`(
id INT(20) PRIMARY KEY,
`name` VARCHAR(40),
`pwd` VARCHAR(40)

)ENGINE =INNODB DEFAULT CHARSET=utf8;

INSERT INTO `user` (id,`name`,pwd)
VALUES(1,'大林','123456'),(2,'小江','123456'),(3,'小罗','1234567');`user`

新建项目

  1. 新建一个普通maven项目

  2. 删除src目录

  3. 导入Maven依赖

    <!--导入依赖-->
        <dependencies>
            <!--导入Mybatis依赖-->
            <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.2</version>
            </dependency>
    
            <!--导入连接数据库依赖-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
            </dependency>
            <!--导入Junit依赖-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
            </dependency>
        </dependencies>
    

    2.2创建一个模块

    • 编写Mybatis核心配置文件
    <?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>
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
    <!--    <mappers>-->
    <!--        <mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
    <!--    </mappers>-->
    </configuration>
    
    • 编写Mybatis工具类
    package com.ljl.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.IOException;
    import java.io.InputStream;
    
    //获取SqlSessionFactory-->SqlSession
    public class MybatisUtils {
    private  static   SqlSessionFactory sqlSessionFactory ;
        static {
             //使用Mybatis工具类获取sqlSessionFactory
            try {
                String resource = "mybatis-config.xml";
                InputStream inputStream = null;
                inputStream = Resources.getResourceAsStream(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
       //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
        //SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
        public static SqlSession getsqlSession(){
            return sqlSessionFactory.openSession();
    
        }
    
    
    }
    

2.3编写代码

  • 实体类
package com.ljl.pojo;

public class User {
    private int id;
    private String name;
    private String password;

    public User() {
    }

    public User(int id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

  • Dao接口
//    获取User
    List<User> getUserList();
  • 接口实现类
<?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=绑定一个对应的Dao/Mapper接口-->
<!--id=绑定一个对应的Dao/Mapper接口的方法名-->

<mapper namespace="com.ljl.dao.UserDao">
   <select id="getUserList" resultType="com.ljl.pojo.User">
       section * from mybatis.user
   </select>
</mapper>

2.4测试

  • junit测试
@Test
public void test(){
    //获取SqlSession对象
    SqlSession sqlSession = MybatisUtils.getsqlSession();
    //执行SQL

    UserDao mapper = sqlSession.getMapper(UserDao.class);

    List<User> userList = mapper.getUserList();

    for (User user : userList) {
        System.out.println(user);
    }
    //关闭SqlSession
    sqlSession.close();
}

3.CRUD

3.1namespace

namespace指向了对应的接口名涵盖包名

3.2select

选择,查询语句

  • id :对应namespace指向接口中的方法名
  • resultType:sql语句执行的返回值
  • parameterType:参数类型

3.3编写UserMapper接口

//    获取User
    List<User> getUserList();

//    根据id获取用户
    User getUserById(int id);

//    插入一个用户
    int addUser(User user);

//    修改用户
    int updateUser(User user);

//    删除用户
    int delUser(User user);

3.4UserMapper.xml中编写sql语句

<!--namespace=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.ljl.dao.UserMapper">
   <select id="getUserList" resultType="com.ljl.pojo.User">
       select * from mybatis.user
   </select>

    <select id="getUserById" resultType="com.ljl.pojo.User" parameterType="int">

        select * from mybatis.user where id = #{id}

    </select>

    <insert id="addUser" parameterType="com.ljl.pojo.User" >

        insert into mybatis.user(id, name, password) values (#{id},#{name},#{password})
    </insert>

    <update id="updateUser" parameterType="com.ljl.pojo.User" >
        update mybatis.user set name=#{name},password=#{password} where id=#{id}
    </update>
    
    <delete id="delUser" parameterType="com.ljl.pojo.User">
        delete from mybatis.user where name=#{name}
    </delete>

</mapper>

3.5测试

package com.ljl.dao;

import com.ljl.pojo.User;
import com.ljl.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;


public class test {
   @Test
   public void getUserList(){
       //获取SqlSession对象
       SqlSession sqlSession = MybatisUtils.getsqlSession();
       //执行SQL
    try {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<User> userList = mapper.getUserList();

        for (User user : userList) {
            System.out.println(user);
        }
        //关闭SqlSession
    }finally {
        sqlSession.close();
    }

   }
@Test
   public void getUserById(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();

    try{
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user = mapper.getUserById(2);

        System.out.println(user.toString());
    }finally {
        sqlSession.close();
    }
}
@Test
public void addUser(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();

    try{
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        int i = mapper.addUser(new User(4, "罗杰", "123456"));
        if (i>0){
            System.out.println("插入成功");
        }
//        增删改业务一定要设置提交事务
        sqlSession.commit();
    }finally {
        sqlSession.close();
    }
}
@Test
public void updateUser(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();

    try{
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int i = mapper.updateUser(new User(1, "darling", "123456"));
        if (i>0){
            System.out.println("更新成功");
        }
//        记得提交事务
        sqlSession.commit();
    }finally {
        sqlSession.close();
    }
}
    @Test
    public void delUser(){
        SqlSession sqlSession = MybatisUtils.getsqlSession();

        try{
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);

            int i = mapper.delUser(new User( 0,"darling",null));
            if (i>0){
                System.out.println("删除成功");
            }
//        增删改业务一定要设置提交事务
            sqlSession.commit();
        }finally {
            sqlSession.close();
        }
    }

}

4.配置解析

4.1核心配置文件

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息.配置文档的顶层结构如下:

4.2环境配置

Mybatis可以配置多套环境

但是SqlSessionFactory实例化时只能选择一种环境

学会配置多套环境

<environment id="development">  id=test则默认使用test下环境配置
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </dataSource>
</environment>
<environment id="test">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </dataSource>
</environment>

Mybatis默认的事务管理器就是JDBC 默认的连接数据库方式 :连接池 POOLED

<transactionManager type="JDBC"/>
    <dataSource type="POOLED">

4.3配置属性优化

  • 这些属性可以在外部进行配置,并可以进行动态替换。
  • 你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。

举列可见Mybatis中文文档,通俗易懂

4.4类型别名(typeAliases)

  • 类型别名可为 Java 类型设置一个缩写名字。
  • 意在降低冗余的全限定类名书写。

举列可见Mybatis中文文档,通俗易懂

4.5设置(settings)(重要)

  • 这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

  • 日志实现

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fx6Bq8PE-1615015284663)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210303155541974.png)]

  • 缓存和懒加载

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yZ4llkGg-1615015284666)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210303155828069.png)]

4.6映射器(mappers)

  • MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了
  • 需要告诉 MyBatis 到哪里去找到这些语句。
  • 最好的办法是直接告诉 MyBatis 到哪里去找映射文件。
  • 你可以使用相对于类路径的资源引用,或完全限定资源定位符,或类名和包名等。

方式一:完全限定资源定位符(常用)

  • 一般xml配置文件放在resources目录下 可以建个mapper文件夹
  • 如果放在了java包下,需要修改maven的配置 可以在任何包下扫描资源 默认是只能在resources目录下扫描资源
<mappers>
    <mapper resource="mapper\UserMapper.xml"/>
</mappers>

方式二:通过包名

  • 包名下的接口名和xml资源名相同
  • 接口名和xml资源必须放在同一个包下
  • 如果我们将xml资源文件放在了resources目录下想使用通过包名的方式映射可以在resources目录下创建与java目录中接口相同的包名然后将xml资源文件放在里面即可
<mappers>
    <package name="com.ljl.dao" />
</mappers>

4.7生命周期和作用域

生命周期类别作用域是至关重要的,因为错误的使用会导致非常严重的并发问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VjgumBbY-1615015284670)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210303172501517.png)]

SqlSessionFactoryBuilder

  • 创建SqlSessionFactory
  • 局部变量

SqlSessionFactory

  • 可以理解为数据库连接池
  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
  • SqlSessionFactory 的最佳作用域是应用作用域
  • 最简单的就是使用单例模式或者静态单例模式。

SqlSession

  • SqlSession的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
  • 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。

5.解决数据库字段名和实体类属性名不一致情况

resultMap(结果集映射)

将映射的字段做出说明即可

  • resultMap 元素是 MyBatis 中最重要最强大的元素
  • ResultMap的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
<resultMap id="UserMap" type="com.ljl.pojo.User">
       <result column="pwd" property="password" />

   </resultMap>
    <select id="getUserList" resultMap="UserMap">
       select * from mybatis.user
   </select>

6.日志

6.1日志工厂

如果一个数据库操作出现了异常,我们需要排错,日志就是最好的工具

  • 以前:deBug soutSql语句
  • 想在:日志工厂!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yGD0myfX-1615015284673)(file://C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20210303155541974.png?lastModify=1614833308)]

  • SLF4J
  • LOG4J (掌握)
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING(掌握)
  • NO_LOGGING

具体使用哪一个日志可以在Mybatis设置中设定

STDOUT_LOGGING 标准日志输出

首先在Mybatis核心配置文件中配置我们要使用的日志工厂

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

查看控制台打印的日志

Opening JDBC Connection
Created connection 292917034.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@11758f2a]
==>  Preparing: select * from mybatis.user 
==> Parameters: 
<==    Columns: id, name, password
<==        Row: 2, 小江, 123456
<==        Row: 3, 小罗, 1234567
<==      Total: 2
User{id=2, name='小江', password='123456'}
User{id=3, name='小罗', password='1234567'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@11758f2a]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@11758f2a]
Returned connection 292917034 to pool.

6.2LOG4J

什么是LOG4J:

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程
  • 我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
  • 通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
  1. 先导入log4j的包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

2.log4j.properties

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/ljl.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

3.Mybatis配置文件中选择log4j为我们所要使用的日志

<settings>
<!--标准日志工厂-->
<!--        <setting name="logImpl" value="STDOUT_LOGGING"/>-->
<!--LOG4J日志-->
    <setting name="logImpl" value="LOG4J"/>
    </settings>

4.测试使用log4j日志

[org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
[org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 497359413.
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1da51a35]
[com.ljl.dao.UserMapper.getUserList]-==>  Preparing: select * from mybatis.user 
[com.ljl.dao.UserMapper.getUserList]-==> Parameters: 
[com.ljl.dao.UserMapper.getUserList]-<==      Total: 2
User{id=2, name='小江', password='123456'}
User{id=3, name='小罗', password='1234567'}
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1da51a35]
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1da51a35]
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 497359413 to pool.

Process finished with exit code 0

简单使用

1.在要是用LOG4J的类中导入包 apach下的包 import org.apache.log4j.Logger;

2.获取日志对象,参数为当前类的class

static Logger logger =Logger.getLogger(test.class);

3.日志级别

logger.info("info:进入了log4jTest方法");
logger.debug("debug:进入了log4jTest方法");
logger.error("error:进入了log4jTest方法");

7.分页(limit)

分页在现在使用非常广泛:可以让我们的界面显示整洁 减少我们数据的处理量

使用Mybatis实现分页处理

1.编写接口

//    使用分页查询用户
    List<User> getUserListByLimit(Map<String,Integer> map);

2.配置Mapper.xml

<select id="getUserListByLimit" parameterType="map" resultType="com.ljl.pojo.User">
    select * from mybatis.user limit #{startIndex},#{pageSize}
</select>

3.测试

@Test
   public void getUserListByLimit(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();

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

    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("startIndex",0);
    map.put("pageSize",3);
    List<User> userList = mapper.getUserListByLimit(map);

    for (User user : userList) {
        System.out.println(user);
    }


    sqlSession.close();
}

8.使用注解开发

8.1CRUD

1.接口

public interface UserMapper {

    @Select("select * from mybatis.user where id=#{id}")
    List<User> getUserListById(@Param("id") int id);

    @Insert("insert into mybatis.user(id,name,password)values(#{id},#{name},#{password})")
    int addUser(@Param("id") int id,@Param("name") String name,@Param("password") String pwd);
}

2.在核心配置文件中绑定我们的接口

<mappers>
    <mapper class="com.ljl.dao.UserMapper"/>
</mappers>

3.测试

 @Test
public void getUserListById(){
     SqlSession sqlSession = MybatisUtils.getsqlSession();
     UserMapper mapper = sqlSession.getMapper(UserMapper.class);
     List<User> userList = mapper.getUserListById(1);
     System.out.println(userList);
     sqlSession.close();
 } @Test
public void addUser(){
     SqlSession sqlSession = MybatisUtils.getsqlSession();
     UserMapper mapper = sqlSession.getMapper(UserMapper.class);
     int i = mapper.addUser(4,"darling","1234567");
     sqlSession.commit();
     sqlSession.close();
 }
  • 注解开发在小项目中可以使用,但是公司一般不会使用注解
  • 所以我们可以只是作为了解学习

关于@Param注解

  • 接本类型的参数和String类型需要加上
  • 应用类型可以不加
  • 如果只有一个基本类型参数时可以不加,但为了统一规范还是加上
  • 我们Sql中引用的参数就是我们定义@Param{“传值参数”}中的参数

8.2面试高频问题:#{ } 和${ }

经常碰到这样的面试题目:#{}和${}的区别是什么?
 
网上的答案是:#{}是预编译处理,$ {}是字符串替换。mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;mybatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。使用 #{} 可以有效的防止SQL注入,提高系统安全性。
 
对于这个题目我感觉要抓住两点:
(1)$ 符号一般用来当作占位符,常使用Linux脚本的人应该对此有更深的体会吧。既然是占位符,当然就是被用来替换的。知道了这点就能很容易区分$和#,从而不容易记错了。
(2)预编译的机制。预编译是提前对SQL语句进行预编译,而其后注入的参数将不会再进行SQL编译。我们知道,SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成了恶意的执行操作。而预编译机制则可以很好的防止SQL注入。
 
最后想说的是,对于mybatis 以及 sql 而言,每一个考点背后都是有一个深刻的思想存在的,应该好好的体会。这样才能真正的做到技术提升,成为技术大牛。

SQL注入案例

${param}传递的参数会被当成sql语句中的一部分,比如传递表名,字段名
 
例子:(传入值为id)
 
order by ${param} 
 
则解析成的sql为:
 
order by id
 
 
 
#{parm}传入的数据都当成一个字符串,会对自动传入的数据加一个双引号
 
例子:(传入值为id)
 
select * from table where name = #{param}
 
则解析成的sql为:
 
select * from table where name = "id"
 
为了安全,能用#的地方就用#方式传参,这样可以有效的防止sql注入攻击
 
 
 
sql注入简介
直接上了百度的例子,感觉一看就清晰明了
 
 
 
某个网站的登录验证的SQL查询代码为:
 
strSQL = "SELECT * FROM users WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
 
恶意填入
 
userName = "1' OR '1'='1";
 
与passWord = "1' OR '1'='1";
 
时,将导致原本的SQL字符串被填为
 
strSQL = "SELECT * FROM users WHERE (name = '1' OR '1'='1') and (pw = '1' OR '1'='1');"
 
也就是实际上运行的SQL命令会变成下面这样的
 
strSQL = "SELECT * FROM users;"
 
这样在后台帐号验证的时候巧妙地绕过了检验,达到无账号密码,亦可登录网站。所以SQL注入攻击被俗称为黑客的填空游戏。

9.多对一处理

多对一:比如说一个班级有很多学生,可是这个班级只有一个班主任。在这个班级中随便找一个人,就会知道他们的班主任是谁;知道了这个班主任就会知道有哪几个学生。这里学生和班主任的关系就是多对一

9.1测试环境搭建

  1. 安装lombok插件 下载lombok依赖
  2. 数据库建设相应的字段表
  3. 建立好项目工程 pojo Teacher和Student
  4. 建立Mapper接口
  5. 建立Mapper.xml配置文件
  6. 核心文件中注册我们的Mapper.xml文件
  7. 测试环境是否搭建成功

9.2按照查询嵌套处理(子查询)

<select id="getStudent" resultMap="StudentTeacher">
        select * from mybatis.student
    </select>
    <resultMap id="StudentTeacher" type="com.ljl.pojo.Student">
        <result property="id" column="id" />
        <result property="name" column="name" />
        <association property="teacher" column="tid" javaType="com.ljl.pojo.Teacher" select="getTeacher" />


    </resultMap>

    <select id="getTeacher" resultType="com.ljl.pojo.Teacher">
        select * from mybatis.teacher where id =#{tid}
    </select>

9.3按照结果嵌套处理(联表查询)

    <!--按照结果嵌套处理-->
<select id="getStudent1" resultMap="StudentTeacher2">
    select s.id sid,s.name sname,t.name tname,t.id tid
    from mybatis.student as s , mybatis.teacher as t
    where s.tid=t.id
</select>
<resultMap id="StudentTeacher2" type="com.ljl.pojo.Student">
    <result column="sid" property="id"/>
    <result column="sname" property="name"/>
    <association property="teacher" javaType="com.ljl.pojo.Teacher">
        <result property="name" column="tname"/>
        <result property="id" column="tid"/>
    </association>
</resultMap>

10一对多处理

一个老师拥有多个学生!对于老师而言,就是一对多的关系!

10.1搭建环境

改变实体类

public class Student {
    private int id;
    private String name;
    private int tid;
}
public class Teacher {
    private int id;
    private String name;
    List<Student> students;
}

10.2按照结果嵌套处理(联表查询)

<select id="getTeacher" resultMap="TeacherStudent" >
    select t.id tid,t.name tname,s.tid stid,s.name sname,s.id sid
    from mybatis.teacher t,mybatis.student s
    where t.id = s.tid and t.id=#{id}
</select>
<resultMap id="TeacherStudent" type="com.ljl.pojo.Teacher">
    <result column="tid" property="id"/>
    <result column="tname" property="name"/>
    <collection property="students" ofType="com.ljl.pojo.Student">
        <result column="stid" property="tid" />
        <result column="sname" property="name"/>
        <result column="sid" property="id"/>
    </collection>
</resultMap>

10.3按照查询嵌套处理(子查询)

<select id="getTeacher1" resultMap="TeacherStudent1">
    select * from mybatis.teacher where id=#{tid}
</select>

<resultMap id="TeacherStudent1" type="com.ljl.pojo.Teacher">
    <collection property="students" ofType="com.ljl.pojo.Student" javaType="ArrayList" select="getStudent" column="id">

    </collection>
</resultMap>

<select id="getStudent" resultType="com.ljl.pojo.Student" >
    select * from mybatis.student where tid =#{tid}
</select>

10.4小结:

1.关联:association 【多对一】
2.集合:collection 【一对多】
3.JavaType && ofType
JavaType : 用来指定实体类中的属性
ofType :用来指定映射到List集合或者集合中的pojo类型,泛型中的约束类型!

注意点:

保证SQL的可读性,尽量保证通俗易懂!
注意一对多和多对一中,属性名和字段的问题!
如果问题不好排查,可以使用日志,建议使用Log4j

11.动态SQL

11.1什么是动态SQL

  • 动态 SQL 是 MyBatis 的强大特性之一。
  • 如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
  • 使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
  • MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
  • 根据不同的需求产生不同的SQL语句
  • 动态SQL其实就是在拼接sql语句,只要我们保证sql语句正确性的前提下排列组合即可

    if
    choose (when, otherwise)
    trim (where, set)
    foreach

11.2环境搭建

  • 创建数据库表
CREATE TABLE `blog`(
	`id` VARCHAR(50) NOT NULL COMMENT '博客id',
	`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
	`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
	`create_time` DATETIME NOT NULL COMMENT '创建时间',
	`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8
  • 创建数据库对应实体pojo
package com.ljl.pojo;

import lombok.Data;

import java.util.Date;
@Data
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}
  • 创建Mapper接口
package com.ljl.dao;

import com.ljl.pojo.Blog;

public interface BlogMapper {
    int addBlog(Blog blog);
}
  • 编写对应的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">
<!--namespace=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.ljl.dao.BlogMapper">

    <insert id="addBlog" parameterType="com.ljl.pojo.Blog">
        insert into mybatis.blog(id, title, author, create_time, views)
        values (#{id},#{title},#{author},#{createTime},#{views})
    </insert>
</mapper>
  • 测试环境搭建插入数据
@Test
public void addBlog(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Blog blog=new Blog();
    blog.setId(idUtils.getId());
    blog.setAuthor("大林");
    blog.setCreateTime(new Date());
    blog.setTitle("MybatisStudy");
    blog.setViews(999999);
   mapper.addBlog(blog);

   blog.setId(idUtils.getId());
   blog.setTitle("SpringStudy");
   mapper.addBlog(blog);

   blog.setId(idUtils.getId());
   blog.setTitle("javaWebStudy");
   mapper.addBlog(blog);

   blog.setId(idUtils.getId());
   blog.setTitle("SpringBootStudy");
   mapper.addBlog(blog);

    sqlSession.commit();
    sqlSession.close();
}

11.3if

<select id="queryBlog" parameterType="map" resultType="com.ljl.pojo.Blog">
    select * from mybatis.blog where 1=1

    <if test="title!=null">
        and title =#{title}
    </if>
    <if test="views!=null">
        and views =#{views}
    </if>
</select>

11.4trim(where ,set)

<select id="queryBlog" parameterType="map" resultType="com.ljl.pojo.Blog">
    select * from mybatis.blog
    <where>
        <if test="title!=null"/>
        and title = #{title}
        <if test="author!=null"/>
        and author=#{author}
    </where>
</select>


<update id="updateBlog" parameterType="map">
    update mybatis.blog
   <set>

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

       <if test="author!=null" >
       author =#{author},
       </if>
       <if test="views!=null">
       views=#{views}
       </if>
   </set>
    where id =#{id}
</update>

11.5SQL片段

有的时候我们会将一些功能的公共部分抽取出来,方便复用

  1. 使用sql标签抽取公共部分
<sql id="if-title-author" >
    <if test="title!=null">
    </if>
    and title = #{title}
    <if test="author!=null">
    and author=#{author}
    </if>
</sql>
  1. 在需要复用的地方加上include标签即可
<select id="queryBlog" parameterType="map" resultType="com.ljl.pojo.Blog">
    select * from mybatis.blog
    <where>
        <include refid="if-title-author"></include>
    </where>
</select>

12.缓存

12.1缓存简介

什么是缓存

将对象/数据存于内存之中,内存释放后,对象/数据也就消失

为什么用缓存

减少和数据库的交互(主要是查询),提升效率

缓存的适用场景

  • 适用场景:
    • 数据比较固定,对结果影响不大的
  • 不适用场景
    • 数据多变,对结果影响大的。例如:股票,抢购商品

12.2一级缓存

Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言。所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。

img

为什么要使用一级缓存,不用多说也知道个大概。但是还有几个问题我们要注意一下。

1、一级缓存的生命周期有多长?

a、MyBatis在开启一个数据库会话时,会 创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象。Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。

b、如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用。

c、如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用。

d、SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用

2、怎么判断某两次查询是完全相同的查询?

mybatis认为,对于两次查询,如果以下条件都完全一样,那么就认为它们是完全相同的两次查询。

2.1 传入的statementId

2.2 查询时要求的结果集中的结果范围

2.3. 这次查询所产生的最终要传递给JDBC java.sql.Preparedstatement的Sql语句字符串(boundSql.getSql() )

2.4 传递给java.sql.Statement要设置的参数值

12.3二级缓存:

MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。

MyBatis的缓存机制整体设计以及二级缓存的工作模式

img

SqlSessionFactory层面上的二级缓存默认是不开启的,二级缓存的开席需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的。 也就是要求实现Serializable接口,配置方法很简单,只需要在映射XML文件配置就可以开启缓存了,如果我们配置了二级缓存就意味着:

  • 映射语句文件中的所有select语句将会被缓存。
  • 映射语句文件中的所欲insert、update和delete语句会刷新缓存。
  • 缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法来收回。
  • 根据时间表,比如No Flush Interval,(CNFI没有刷新间隔),缓存不会以任何时间顺序来刷新。
  • 缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用
    …(img-THGxdTD3-1615015284678)]

为什么要使用一级缓存,不用多说也知道个大概。但是还有几个问题我们要注意一下。

1、一级缓存的生命周期有多长?

a、MyBatis在开启一个数据库会话时,会 创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象。Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。

b、如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用。

c、如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用。

d、SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用

2、怎么判断某两次查询是完全相同的查询?

mybatis认为,对于两次查询,如果以下条件都完全一样,那么就认为它们是完全相同的两次查询。

2.1 传入的statementId

2.2 查询时要求的结果集中的结果范围

2.3. 这次查询所产生的最终要传递给JDBC java.sql.Preparedstatement的Sql语句字符串(boundSql.getSql() )

2.4 传递给java.sql.Statement要设置的参数值

12.3二级缓存:

MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。

MyBatis的缓存机制整体设计以及二级缓存的工作模式

[外链图片转存中…(img-DO35M3PA-1615015284680)]

SqlSessionFactory层面上的二级缓存默认是不开启的,二级缓存的开席需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的。 也就是要求实现Serializable接口,配置方法很简单,只需要在映射XML文件配置就可以开启缓存了,如果我们配置了二级缓存就意味着:

  • 映射语句文件中的所有select语句将会被缓存。
  • 映射语句文件中的所欲insert、update和delete语句会刷新缓存。
  • 缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法来收回。
  • 根据时间表,比如No Flush Interval,(CNFI没有刷新间隔),缓存不会以任何时间顺序来刷新。
  • 缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用
  • 缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全的被调用者修改,不干扰其他调用者或线程所做的潜在修改。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值