自学内容网 自学内容网

JavaEE初学07

Mybatis

Mybatis是一款优秀的持久层框架,他支持自定义SQL、存储过程以及高级映射。Mybatis几乎免除了所有的JDBC代码以及设置参数和获取结果集的工作。Mybatis可以通过简单的XML或注解来配置和映射原始类型、接口和Java POJO(Plain Old Java Object,普通老式Java对象)为数据库中的记录。

获取数据库连接有两种方式:(1)DriveManager(2)数据库连接池
区别:
DriveManager每次都创建物理连接,关闭物理连接
数据库连接池在每次初始化时就创建一定数量的数据库连接,每次都从连接池获取连接,每次关闭不关闭物理连接,只是放回连接池(复用提高效率)

项目中,使用双中校检锁的单例模式,创建的是数据库连接池

操作命令对象:(1)简单的 (2)预编译 (3)存储过程
预编译可以提高性能,并且可以防止sql注入(替换占位符,字符串替换会进行转义)

使用Mybatis封装jdbc后,只需要提供: 要执行的SQL语句、要替换占位符的数据(一般为Java对象)、处理结果集(一般转化为Java对象)(查询时返回结果集、增删改时返回更新数量)其它如执行SQL、返回结果集、返回更新数量的步骤由框架来提供

ORM

ORM(Object Relational Mapping)即对象关系映射,将关系型数据库中的数据和对象建立起映射关系,进而自动的完成数据与对象的互相转换
ORM把数据库映射为对象:

数据库表(table)                --->         类(class)
记录(record,行数据)            --->         对象(object)
字段(field)                     --->         对象的属性(attribute)

一般的ORM框架,会将数据库模型中每张表都映射为一个类

常见的ORM框架:Mybatis、Hibernate
Mybatis:半自动框架,自己写sql,更方便做sql的性能维护和优化,对关系型数据库模型要求不高,做调整时影响不会很大
缺点:不能跨数据库

Hibernate:全自动框架,自动组装sql语句,可以跨数据库,框架提供了多套主流数据库sql语句生成规则
缺点:学习成本高、对数据库模型依赖很大,需求频繁变更会很难维护和修改、很难定位问题、很难进行性能优化

Mybatis

Mybatis操作配置步骤
创建Maven项目,配置pom.xml文件
准备数据库初始化sql

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>

    <!-- 默认使用的Spring Framework版本为5.2.10.RELEASE -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.example</groupId>
    <artifactId>mybatis-study</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <!-- spring-boot-starter-web: 基于SpringBoot开发的依赖包,
                                 会再次依赖spring-framework中基本依赖包,aop相关依赖包,web相关依赖包,
                                 还会引入其他如json,tomcat,validation等依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!-- 排除tomcat依赖 -->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- 添加 Undertow 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>

        <!--引入AOP依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <!-- mybatis-spring-boot-starter: Mybatis框架在SpringBoot中集成的依赖包,
                                Mybatis是一种数据库对象关系映射Object-Relationl Mapping(ORM)框架,
                                其他还有如Hibernate等 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>

        <!-- Mybatis代码生成工具 -->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.5</version>
        </dependency>

        <!-- druid-spring-boot-starter: 阿里Druid数据库连接池,同样的运行时需要 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.3</version>
        </dependency>

        <!-- JDBC:mysql驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
            <scope>runtime</scope>
        </dependency>

        <!-- spring-boot-devtools: SpringBoot的热部署依赖包 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <!-- 不能被其它模块继承,如果多个子模块可以去掉 -->
            <optional>true</optional>
        </dependency>

        <!-- lombok: 简化bean代码的框架 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- spring-boot-starter-test: SpringBoot测试框架 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- SpringBoot的maven打包插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <!-- 明确指定一些插件的版本,以免受到 maven 版本的影响 -->
            <plugin>
                <artifactId>maven-clean-plugin</artifactId>
                <version>3.1.0</version>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
            </plugin>
            <plugin>
                <artifactId>maven-deploy-plugin</artifactId>
                <version>2.8.2</version>
            </plugin>
            <plugin>
                <artifactId>maven-install-plugin</artifactId>
                <version>2.5.2</version>
            </plugin>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.0</version>
            </plugin>
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.1.0</version>
            </plugin>
            <plugin>
                <artifactId>maven-site-plugin</artifactId>
                <version>3.3</version>
            </plugin>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>

        </plugins>
    </build>

</project>

sql创建

drop database if exists mybatis_study;
create database mybatis_study character set utf8mb4;

use mybatis_study;

drop table if exists user;
create table user(
                     id int primary key auto_increment,
                     username varchar(20) not null unique comment '账号',
                     password varchar(20) not null comment '密码',
                     nickname varchar(20) comment '用户昵称',
                     sex bit default 0 comment '性别,0/false为女,1/true为男',
                     birthday date comment '生日',
                     head varchar(50) comment '头像地址',
                     create_time timestamp default now() comment '创建日期,默认为插入时的日期'
) comment '用户表';

drop table if exists article;
create table article(
                        id int primary key auto_increment,
                        title varchar(20) not null comment '文章标题',
                        content mediumtext not null comment '文章内容',
                        view_count int default 0 comment '文章浏览量',
                        user_id int comment '外键:用户id',
                        create_time timestamp default now() comment '创建日期,默认为插入时的日期',
                        foreign key(user_id) references user(id)
) comment '文章表';

insert into user(username, password) values ('a1', '11');
insert into user(username, password) values ('a2', '12');
insert into user(username, password) values ('b', '2');
insert into user(username, password) values ('c', '3');

insert into article(title, content, user_id) value ('快速排序', 'public ...', 1);
insert into article(title, content, user_id) value ('冒泡排序', 'private ...', 1);
insert into article(title, content, user_id) value ('选择排序', 'private ...', 1);
insert into article(title, content, user_id) value ('归并排序', 'public ...', 2);
insert into article(title, content, user_id) value ('插入排序', 'protected ...', 2);
insert into article(title, content, user_id) value ('希尔排序', 'protected ...', 3);
insert into article(title, content, user_id) value ('List', 'public ...', 4);
insert into article(title, content, user_id) value ('Set', 'public ...', 4);
insert into article(title, content, user_id) value ('Map', 'public ...', 4);

创建启动类

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}

配置文件

logging.level.root=debug
logging.level.druid.sql.Statement=DEBUG
logging.level.org.example=DEBUG
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_study?useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
mybatis.mapper-locations=classpath:mapper/**Mapper.xml

创建数据库实体类

@Getter
@Setter
@ToString
public class User {
    private Integer id;
    private String username;
    private String password;
    private String nickname;
    private boolean sex;
    private Date birthday;
    private String head;
    private Date create_time;
}
@Getter
@Setter
@ToString
public class Article {
    private Integer id;
    private String title;
    private String content;
    private Integer view_count;
    private Integer user_id;
    private Date create_time;
}

创建mapper接口方法
在这个接口方法里,返回值就是jdbc操作后的返回:
查询时:如果结果集是一行数据,则返回一个对象,若查不到则返回null
如果结果集是多行数据,则返回一个List<类型>,查不到则返回一个空的List<类型>
更新时:返回int,更新多少条就返回多少
方法参数:要替换占位符的值

@Mapper
@Component
public interface ArticleMapple {
    Article selectById(Integer id);
}
@Mapper
@Component
public interface UserMapper {
    User selectById(Integer id);
}

配置实体映射文件
application.properties配置文件中配置的文件mybatis.mapper-locations=classpath:mapper/**Mapper.xml
在这里插入图片描述
ArticleMapper.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="org.example.mapper.ArticleMapper">
    <resultMap id="BaseResultMap" type="org.example.model.Article">
        <id column="id" property="id"/><!--主键字段-->
        <result column="title" property="title"/><!--普通字段-->
        <result column="content" property="content"/>
        <result column="view_count" property="view_count"/>
        <result column="user_id" property="user_id"/>
        <result column="creat_time" property="creat_time"/>
    </resultMap>
</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="org.example.mapper.UserMapper">
    <resultMap id="BaseResultMap" type="org.example.model.User">
        <id column="id" property="id"/><!--主键字段-->
        <result column="username" property="username"/><!--普通字段-->
        <result column="password" property="password"/>
        <result column="nickname" property="nickname"/>
        <result column="sex" property="sex"/>
        <result column="birthday" property="birthday"/>
        <result column="head" property="head"/>
        <result column="creat_time" property="creat_time"/>
    </resultMap>
</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="org.example.mapper.ArticleMapper">
    <resultMap id="BaseResultMap" type="org.example.model.Article">
        <id column="id" property="id"/><!--主键字段-->
        <result column="title" property="title"/><!--普通字段-->
        <result column="content" property="content"/>
        <result column="view_count" property="view_count"/>
        <result column="user_id" property="user_id"/>
        <result column="creat_time" property="creat_time"/>
    </resultMap>


    <!--增删改查对应 <insert>、<delete>、<update>、<select>。方法名和id一致-->
    <select id="selectById" resultMap="BaseResultMap">
        select * from article where id = #{id}
    </select>


</mapper>

UserMapper.xml 同理,添加对应select标签
当sql操作为更新操作时,标签内容应变为:

    <update id="updateContentById" parameterType="org.example.model.Article">
        update article set content = #{content} where id = #{id}
    </update>

当接口方法的方法参数为多个时,则需要添加@Param注解,如:

    List<Article> selectLikeByTitleAndContent(@Param("title")String title , @Param("content")String content);

传入参数时,有时不需要添加单引号(如排序时desc(倒序)和asc(正序)),则需要使用${}来进行拼接,如:

List<Article> selectLikeByUserIdAndContentOrderBy(@Param("user_id")Integer user_id,@Param("content")String content,@Param("orderBy")String orderBy);
    <select id="selectLikeByUserIdAndContentOrderBy" resultMap="BaseResultMap">
        select * from article where user_id = #{user_id} and content like #{content} order by id ${orderBy}
    </select>
    @Test
    public void selectLikeByUserIdAndContentOrderBy(){
        List<Article> list = articleMapper.selectLikeByUserIdAndContentOrderBy(1,"p%","asc");
        System.out.println(list);
    }

增加数据时获取主键
useGeneratedKeys 这会使用JDBC的getGeneratedKeys 方法来获取由数据库内部生成的主键的值
keyProperty 指定能够唯一识别对象的属性

int insertGetIdKey(Article article);
    <insert id="insertGetIdKey" parameterType="org.example.model.Article" useGeneratedKeys="true" keyProperty="id">
        insert into article(title,content,user_id) values (#{title},#{content},#{user_id})
    </insert>
    @Test
    public void insertGetIdKey(){
        Article article = new Article();
        article.setTitle("123");
        article.setContent("123456");
        article.setUser_id(1);
        articleMapper.insertGetIdKey(article);
        System.out.println(article.gerId());//id有值
    }

查询结果集,包含两张表,对应两个JAVA类型的数据,他们之间产生了一对一,一对多的关系
如:
select * from user u join article a on u.id = a.user_id
在这里插入图片描述

一对一结果映射

返回List<Article>,以文章返回的主体,就应该表现为一个文章对应一个用户,一对一映射关系

public class Article{
private User user;
}
public interface ArticleMapper {
List<Article> selectAll();//查询所有文章信息,并携带用户信息,一对一
}
<resultMap>
<!--把Article类中的user属性,建立一对一映射关系 property选择对应属性 columnPrefix前缀匹配(查找后哪个信息是user属性的属性) resultMap结果集映射-->
    <association property="user" columnPrefix="u_" resultMap="org.example.mapper.UserMapper.BaseResultMap"/>
    </resultMap>
    <select id="selectAll" resultMap="BaseResultMap">
        select a.*,u.id u_id,u.username u_username,u.password u_password,u.nickname u_nickname,u.sex u_sex,u.birthday u_birthday,u.head u_head,u.create_time u_create_time from user u join article a on u.id = a.user_id
    </select>
    @Test
    public void selectAll(){
        List<Article> articles = articleMapper.selectAll();
        articles.forEach(System.out::println);
    }
一对多结果映射

因为一个用户对应多个文章,所以返回的类型应该为List<User>,

public class User{
List<Article> articles;
}
    List<User> selectAll1();
<mapper namespace="org.example.mapper.UserMapper">
    <resultMap id="BaseResultMap" type="org.example.model.User">
    <!--一对多映射(此处标签名与一对一映射标签名不一致  注意)-->
        <collection property="article" columnPrefix="a_" resultMap="org.example.mapper.ArticleMapper.BaseResultMap"/>
    </resultMap>
    <select id="selectAll1" resultMap="BaseResultMap">
        select u.*,a.id a_id,a.title a_title,a.content a_content,a.view_count a_view_count,a.user_id a_user_id,a.create_time a_create_time from article a join user u on a.user_id = u.id
    </select>
    @Test
    public void selectAll1(){
        List<User> users = userMapper.selectAll1();
        users.forEach(System.out::println);
    }

association 一对一标签 对象中包含另一个对象
collection 一对多标签 对象中包换List<另一个对象>


框架本质上是在运行期基于接口生成代理类,把接口方法生成具体的实现。找到sql,执行sql前替换占位符,执行完转换为java对象。

Mybatis动态sql

本质上就是根据不同条件,循环来动态的拼接sql语句

if标签

在插入一条数据时,有时会存在空的属性,此时如果插入为null,则不能自动插入为默认值,如果需要自动变为默认值,则不应该插入。此时就可以使用<if>
如:

    <insert id="insertIf" useGeneratedKeys="true" keyProperty="id">
        insert into article(
        title,content,user_id
        <if test="view_count!=null">
        ,view_count
        </if>
        ) values (
        #{title},#{content},#{user_id}
        <if test="view_count!=null">
        ,#{view_count}
        </if>)
    </insert>
trim标签

通常情况下,一个字段玄天时,一般使用if标签,多个字段需要选填时,一般使用trim标签
标签属性如下:
prefix:表示整个语句块外部,以prefix的值作为前缀
suffix:表示整个语句块外部,以suffix的值作为后缀
prefixOverrides:表示整个语句块内部需要去除的前缀
suffixOverrides:表示整个语句块内部需要去除的后缀

    <insert id="insertTrim" useGeneratedKeys="true" keyProperty="id">
        insert into article
        <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="title!=null">title,</if>
        <if test="content!=null">content,</if>
        <if test="user_id!=null">user_id,</if>
        <if test="view_count!=null">view_count,</if>
        </trim>
        values
        <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="title!=null">#{title},</if>
        <if test="content!=null">#{content},</if>
        <if test="user_id!=null">#{user_id},</if>
        <if test="view_count!=null">#{view_count},</if>
        </trim>
    </insert>
where标签

在使用trim标签和if标签时,会存在一种trim标签中全部都是if标签,如果此时if标签全为null,那么就没有对应的条件,这样一来就会出现sql语法错误,此时就可以使用where标签。
where可理解为<trim prefix="where" prefixOverrides="and">
如:

    <select id="selectByCondition" resultMap="BaseResultMap">
        select * from user
        <where>
            <if test="id!=null">
                and id = #{id}
            </if>
            <if test="username!=null">
                and username = #{username}
            </if>
            <if test="password!=null">
                and password = #{password}
            </if>
            <if test="nickname!=null">
                and nickname = #{nickname}
            </if>
            <if test="sex!=null">
                and sex = #{sex}
            </if>
            <if test="birthday!=null">
                and birthday = #{birthday}
            </if>
            <if test="head!=null">
                and head = #{head}
            </if>
            <if test="create_time!=null">
                and create_time = #{create_time}
            </if>
        </where>
set标签

根据传入的用户对象属性来更新用户数据,可以使用<set>标签来指定动态内容
根据传入的id属性,来修改(更新)其它不为null的属性
具体操作为:

<update id="updateBySet" parameterType="org.example.model.Article">
        update article
        <set>
            <trim suffixOverrides=",">
                <if test="title!=null">
                    title = #{title},
                </if>
                <if test="content!=null">
                    content = #{content},
                </if>
                <if test="view_count!=null">
                    view_count = #{view_count},
                </if>
                <if test="user_id!=null">
                    user_id = #{user_id},
                </if>
                <if test="create_time!=null">
                    create_time = #{create_time},
                </if>
            </trim>
        </set>
        where id = #{id}
    </update>
foreach标签

对集合进行遍历可使用foreach标签,foreach有以下属性
collection:绑定方法参数中的集合,如List、Set、Map或数组对象
item:遍历时的每一个对象
open:语句开头的字符串
close:语句结束的字符串
separator:每次遍历之间间隔的字符串
如:批量删除:

    int deleteByIds(List<Integer> ids);
    <delete id="deleteByIds" >
        delete from article where id in
        <foreach collection="list" item="item" open="(" close="_" separator=",">
            #{item}
        </foreach>
    </delete>

批量插入:

    int insertBatch(List<Article> articles);
    <insert id="insertBatch">
        insert into article (title,content,user_id) values
        <foreach collection="list" item="item" separator=",">
            (#{item.title},#{item.content},#{item.user_id})
        </foreach>
    </insert>
    @Test
    public void insertBatch() {
        Article a1 = new Article();
        a1.setTitle("l");
        a1.setContent("llllll");
        a1.setUser_id(1);
        Article a2 = new Article();
        a2.setTitle("i");
        a2.setContent("iiiiiii");
        a2.setUser_id(2);
        Article a3 = new Article();
        a3.setTitle("t");
        a3.setContent("ttttttt");
        a3.setUser_id(3);
        System.out.println(articleMapper.insertBatch(Arrays.asList(a1,a2,a3)));
        List<Article> articles = articleMapper.selectAll();
        articles.forEach(System.out::println);
    }
补充

JDBC的操作步骤
(1)创建数据库连接池DataSource
(2)通过DataSource获取数据库链接Connection
(3)编写执行带?占位符的SQL语句
(4)通过Connection及SQL创建操作命令对象Statement
(5)替换占位符:指定要替换的数据库字段类型,占位符索引及要替换的值
(6)使用Statement执行SQL语句
(7)查询操作:返回结果集ResultSet,更新操作:返回更新的数量
(8)处理结果集
(9)释放资源


占位符
占位符要替换的值:传入的java对象:(插入的值、修改的值、条件字段的值)


数据库初始化

打开数据库配置界面
在这里插入图片描述
点击左上角+号 选择Mysql
在这里插入图片描述
选择驱动程序
在这里插入图片描述
下载驱动文件
在这里插入图片描述
输入用户名密码后点击测试链接,出现绿色勾后点击确定建立连接
在这里插入图片描述
右击sql文件,点击RUN
在这里插入图片描述
点击加号 双击刚才建立的连接 点击确定就初始化完成了
在这里插入图片描述


配置启动类

在这里插入图片描述

在这里插入图片描述

配置完映射文件后,如果需要测试相关配置是否正确,可以在test目录下来进行测试
操作如下:
创建测试类
在这里插入图片描述

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@Transactional//使用事务在测试代码,会自动回滚
public class ArticleMapperTest {
    @Autowired
    private ArticleMapper articleMapper;
    @Test
    public void selectById(){
        Article a = articleMapper.selectById(1);
        System.out.println(a);
    }
}

右击运行即可
在这里插入图片描述

特殊查找
若查找为模糊查找可以用下列两种方法,
(1)可直接把字符串写为"select * from user where username like #{username}" 传入参数时username=“a%”
(2)也可在sql语句中进行拼接select * from user where username like concat(${username},'%') 但是,这种方式存在sql注入的风险


如果只有一个参数(类型为包装类型、基本数据类型),xml中变量名不作要求(xml中变量名可以随便写,如:id=#{aaa}也可以直接获取到)
如果只有一个参数,且类型为POJO类型,xml中,占位符变量名为对象中的属性
如果有多个参数,必须在接口方法参数上,使用@Param("变量名")
如果有多个参数(类型为包装类型、基本数据类型),xml中应写为#{变量名}
如果只有多个参数,且类型为POJO类型,xml中应写为#{变量名.属性}

当被Param注解时,注解内容名称就必须跟xml中通配符的名称一致

多个参数且类型为POJO:

List<Article> selectLikeByUserIdAndContent(@Param("user") User user , @Param("content")String content);
    <select id="selectLikeByUserIdAndContent" resultMap="BaseResultMap">
        select * from article where user_id = #{user.id} and content like #{content}
    </select>

占位符
#{变量名} 会先替换为?,再替换值,所以,替换的字符串值,会带上单引号来替换
${变量名} 会以字符串拼接的方式来替换,所以,替换的字符串值,没有单引号,故存在sql注入的风险


原文地址:https://blog.csdn.net/weixin_52793965/article/details/143719925

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!