MyBatis学习(三)之MyBatis 进阶

记录自己在狂神说java中的学习情况,文章里有自己学习的理解和扩展,新手难免有理解偏差或者错误,恳请大佬指正。


继承上一次学习的代码。

1.万能的map

如果类的种类特别多,或者数据库中的表、字段或者参数过多,类型也特别复杂,那么可以把类型的确定交给真正调用的端确认。通过map可以随意写参数,想怎么定制怎么定制。

在UserMapper接口里新创一个方法:

//使用map来增加用户
boolean insertUserUseMap(Map<String, Object> map);
//使用map根据ID查询用户
User getUserByIdUseMap(Map<String,Object> map);

在对应的mapper.xml文件里用万能的map写参数

<!--使用map后,这里的userId,userName,password可以自己定义名称-->
<insert id="insertUserUseMap" parameterType="map">
    insert into mybatis.user (id,name,pwd) values (#{userId},#{userName},#{password})
</insert>
<select id="getUserByIdUseMap" parameterType="map" resultType="com.hj.pojo.User">
    select * from mybatis.user where id=#{id} and name=#{name};
</select>

然后在测试文件里写测试代码:

@Test
public void insertUserUseMap() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Map<String, Object> map = new HashMap<>();
    map.put("userId", 5); //这里的键名要和mapper.xml里一致
    map.put("userName", "李飞");
    map.put("password", "485615");
    boolean flag = mapper.insertUserUseMap(map);
    if (flag) System.out.println("插入成功");
    sqlSession.commit();
    sqlSession.close();
}
@Test
public void getUserByIdUseMap() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    Map<String, Object> map = new HashMap<>();
    map.put("id", 3);
    map.put("name", "王五");
    User user =mapper.getUserByIdUseMap(map);
    System.out.println(user);
    sqlSession.close();
}

小结:

Map传递参数,直接在sql中取出key即可!【parameterType=“map”】

对象传递参数,直接在sql中取出对象的属性即可!【parameterType=“Object”】

只有一个基本类型参数的情况下,可以直接在sql中取到!多个参数用Map,或者注解!

思考题:模糊查询怎么写?

就是sql里的like语句。在java代码执行的时候传递通配符%%或者在sql拼接中使用通配符%%

接口中:

//模糊查询
List<User> getUserLike(String value);

接口对应的mapper.xml文件中:

这是要在java代码执行的时候传递通配符

<select id="getUserLike" resultType="com.hj.pojo.User">
    select * from mybatis.user where name like #{value};
</select>

对应的测试代码

@Test
public void getUserLike(){
    SqlSession sqlSession=MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    List<User> userList =mapper.getUserLike("%李%");

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

这是在sql拼接中使用通配符

<select id="getUserLike" resultType="com.hj.pojo.User">
    select * from mybatis.user where name like "%"#{value}"%";
</select>

对应的测试代码

@Test
public void getUserLike(){
    SqlSession sqlSession=MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    List<User> userList =mapper.getUserLike("李");

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

再思考:Mybatis是怎么防止sql注入的?

什么是sql注入?sql注入是一种代码注入技术,用于攻击数据驱动的应用,恶意的SQL语句被插入到执行的实体字段中。攻击者在界面的表单信息或URL上输入一些奇怪的SQL片段(例如“or ‘1’=’1’”这样的语句),有可能入侵参数检验不足的应用程序。所以,在我们的应用中需要做一些工作,来防备这样的攻击方式。在一些安全性要求很高的应用中(比如银行软件),经常使用将SQL语句全部替换为存储过程这样的方式,来防止SQL注入。这当然是一种很安全的方式,但我们平时开发中,可能不需要这种死板的方式。

那么mybatis是怎么防止sql注入的呢?

这里还要提到mybatis中**#{}和${}的区别**

  • #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。
    如:where username=#{username},如果传入的值是111,那么解析成sql时的值为where username=“111”, 如果传入的值是id,则解析成的sql为where username=“id”。如果传入的值是**;drop table user;,则解析成的sql为:select id, username, password, role from user where username=“;drop table user;“,很显然表里不会有name为“;drop table user;”**的记录,所以不会返回任何查询结果。

  • $将传入的数据直接显示生成在sql中。

    如:where username=${username},如果传入的值是111,那么解析成sql时的值为where username=111;如果传入的值是;drop table user;,则解析成的sql为:**select id, username, password, role from user where username=;drop table user;**这就是被注入了,删除user表。

  • #方式能够很大程度防止sql注入,$方式无法防止Sql注入。因为#会自动把我们传入的值在外面加入“”号。

  • $方式一般用于传入数据库对象,例如传入表名。

  • 一般能用#的就别用 , 若 不 得 不 使 用 “ ,若不得不使用“ 使{xxx}”这样的参数,要手工地做好过滤工作,来防止sql注入攻击。

  • 在MyBatis中,“ x x x ” 这 样 格 式 的 参 数 会 直 接 参 与 S Q L 编 译 , 从 而 不 能 避 免 注 入 攻 击 。 但 涉 及 到 动 态 表 名 和 列 名 时 , 只 能 使 用 “ {xxx}”这样格式的参数会直接参与SQL编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“ xxxSQL使{xxx}”这样的参数格式。所以,这样的参数需要我们在代码中手工进行处理来防止注入。

总结:在编写MyBatis的映射语句时,尽量采用“#{xxx}”这样的格式。若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止SQL注入攻击。

MyBatis的SQL是一个具有“输入+输出”的功能,类似于函数的结构。在mapper标签下,parameterType表示了输入的参数类型,resultType表示了输出的参数类型。这首先限制了我们输入输出的参数类型,无法肆意妄为。另外使用#的即输入参数在SQL中拼接的部分,传入参数后,打印出执行的SQL语句,会看到SQL是这样的:

select id, username, password, role from user where username=? and password=?

不管输入什么参数,打印出的SQL都是这样的。这是因为MyBatis启用了预编译功能,在SQL执行前,会先将上面的SQL发送给数据库进行编译;执行时,直接使用编译好的SQL,替换占位符“?”就可以了。因为SQL注入只能对编译过程起作用,所以这样的方式就很好地避免了SQL注入的问题。

原理:MyBatis是如何做到SQL预编译的呢?其实在框架底层,是JDBC中的PreparedStatement类在起作用,PreparedStatement是我们很熟悉的Statement的子类,它的对象包含了编译好的SQL语句。这种“准备好”的方式不仅能提高安全性,而且在多次执行同一个SQL时,能够提高效率。原因是SQL已编译好,再次执行时无需再编译。简单说,#{}是经过预编译的(相当于JDBC中的PreparedStatement),是安全的;${}是未经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入。

本小节代码

https://github.com/IPostYellow/mybatisStudyRecords
中的mybatis-03

2.配置解析

官方文档中有XML配置详细说明:https://mybatis.org/mybatis-3/zh/configuration.html

这里只做一些常见内容的简单描述,完整详情可以参考官方文档。

对于核心配置文件

就是之前用过的mybatis-config.xml。Mybatis的配置文件包含了会影响Mybatis行为的设置和属性信息,从官方文档中可以看到以下信息:

  • configuration(配置)
    • properties(属性)
    • settings(设置)
    • typeAliases(类型别名)
    • typeHandlers(类型处理器)
    • objectFactory(对象工厂)
    • plugins(插件)
    • environments(环境配置)
      • environment(环境变量)
        • transactionManager(事务管理器)
        • dataSource(数据源)
    • databaseIdProvider(数据库厂商标识)
    • mappers(映射器)

对于配置环境(environments)

MyBatis可以配置成适应多种环境,但是尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。MyBaits默认的事务管理器是JDBC(总共可以有JDBC、MANAGED两种选择),数据源是连接池POOLED(总共有UNPOOLED、POOLED、JNDI三种选择)。

具体的详细内容可以看到 https://mybatis.org/mybatis-3/zh/configuration.html 中的environments部分。

这些都是死的,当你需要用到某一种的时候,去官方文档查询即可。

对于属性(properties)

我们可以通过properties属性来实现引用配置文件,这些属性都是可外部配置且可动态替换的,既可以在典型的java属性文件中配置,也可通过properties元素的子元素来传递。

比如,第一步。在resources文件夹下新建一个db.properties,内容如下:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8
username=root
password=root

第二步,在核心配置文件中引入properties文件。

<!--引入外部配置文件-->
<properties resource="db.properties">
    <property name="user" value="root"/>
    <property name="password" value="123456"/>
</properties>

这里既可以用外部的db.properties文件里的变量,也可以用自己<property>标签里的变量,如果变量name相同,则默认先用外部文件的变量。

<environment id="development">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
        <!--mysql 8.x 是com.mysql.cj.jdbc.Driver-->
        <property name="driver" value="${driver}"/>
        <!--连接数据库的时候有个url,然后具体到mybatis这个数据库,然后使用ssl,编码utf8等等设置-->
        <property name="url"
                  value="${url}"/>
        <property name="username" value="${user}"/>
        <property name="password" value="${password}"/>
    </dataSource>
</environment>

另外注意在核心配置文件中,这些标签是有先后顺序之分的,必须按照这个顺序写,不然会报错。

在这里插入图片描述

  • 可以直接引入外部文件db.properties
  • 也可以自己新增属性
  • 如果外部引入和自己新增属性的名字相同,默认用外部引入的

对于类型别名(typeAliases)

类型别名是为Java类型设置一个短的名字。它只和XML配置有关,存在的意义仅在于用来减少类完全限定名的冗余。

比如,我们原本的mapper.xml文件里,resultType使用了我们的User类,要使用完全限定名com.hj.pojo.User

<select id="getUserList" resultType="com.hj.pojo.User">
    select * from mybatis.user;
</select>

现在可以使用typeAliases来进行别名修改。

    <!--可以给实体类起别名-->
    <typeAliases>
        <!--自定义别名-->
        <typeAlias type="com.hj.pojo.User" alias="User"/>
        <!--扫描这个包,默认别名为类的小写名(但是大写也可以)-->
        <package name="com.hj.pojo"/>
    </typeAliases>

那么对应的mapper.xml里的内容可以修改为:

<!--对应自定义别名-->
<select id="getUserList" resultType="User">
    select * from mybatis.user;
</select>

或者

<!--对应扫描包-->
<select id="getUserList" resultType="user">
    select * from mybatis.user;
</select>

在实体类比较少的时候,可以用自定义别名,如果实体类比较多,则可以使用第二种。

第一种方式本身可以自己随意起别名,第二种方式本身只能取类名作为别名。(但是可以通过注解再自定义别名)

对于设置(settings)

这是MyBatis中极为重要的调整设置,它们会改变MyBatis的运行时行为。完整的详情可以参考官方文档。

下面仅列出其中几个比较常用的。

设置名描述有效值默认值
cacheEnabled全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。true/falsetrue
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。true/falsefalse
logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J|LOG4J|LOG4J2|JDK_LOGGING
|COMMONS_LOGGING
|STDOUT_LOGGING|NO_LOGGING
未设置

对于映射器(mappers)

MapperRegistry:注册绑定我们的Mapper文件

在这里插入图片描述

注意:使用class或者包扫描来绑定的时候,是有要求的:

  • 接口和他的mapper配置文件必须同名
  • 接口和他的mapper配置文件必须在同一个包下

其他配置

  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins(插件)
    • mybatis-generator-core(逆向工程,自动生成mapper、dao、entity)
    • mybatis-plus(加速用的)
    • 通用mapper

本小节代码

https://github.com/IPostYellow/mybatisStudyRecords
中的mybatis-04

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值