MyBatis学习

简介

相比ORM领域的Hibernate来说, mybatis是一个半自动化的持久化框架. 相比于前者严格的封装, mybatis则灵活的多, 提供给开发者更多的入口去定制化你的映射规则.

MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

框架使用步骤总结

  1. 配置全局的配置文件mybatis-config.xml (核心2块: 1、数据源配置,2、外部的mapper映射文件的配置)
  2. 创建SqlSessionFactory
  3. 通过SqlSessionFactory创建SqlSession对象
  4. 通过SqlSession操作数据库 CRUD
  5. 调用session.commit()提交事务(事务可以设置为手工控制)
  6. 调用session.close()关闭会话

Q & A :

一. 驼峰式命名映射规则:

在mybatis转换ResultSet对象到我们Pojo业务对象时, 通常需要做三件事:

  • 解决数据库列名到Pojo属性名间的映射;
  • 解决数据库字段类型到Java类型的转换工作;
  • 在转换过程中具备一定的容错能力.

核心逻辑即:

  • 数据库中的字段名,如何与对象中的字段关联映射起来.
  • 数据库中的字段类型, 如何相应的转换为Java类型, 而不会引起转换失败.

驼峰式命名的三种解决方案:

  • 1.在xxMapper.xml中 的select语句中, 对需要转换的数据库字段起一个别名(sql中使用AS语法).
<!-- mapper:根标签, namespace:命名空间, 随便写,一般保证命名空间唯一 -->
<mapper namespace="MyMapper">
    <!--
    statement,n内容: sql语句.
    id:唯一标识,随便写,在同一个namespace下保持唯一
    resultType: sql查询结果集的封装类型
    tb_user 即为数据库中的表.
     -->
    <select id="selectUser" resultType="com.david.mybatis.pojo.User">
        -- select * from tb_user where id = #{id}
        SELECT
            tb_user.id,
            tb_user.user_name as userName,
            tb_user.`password`,
            tb_user.birthday,
            tb_user.created
        FROM
            tb_user
        WHERE
            tb_user.id = #{id}
    </select>
</mapper>
  • 2. mybatis项目主配置文件mybatis-config.xml中中设置驼峰开关开启
    TIPs:
    根据dtd文件的规范, 顶层标签在配置文件中的位置有严格的限制, setting标签必须置于properties下方(紧挨着)方可.
    true : 开启驼峰解析
    false: 关闭驼峰解析
<settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
  • 3. 设置resultMap

二. mapper.xml 中sql注释报错:

现象是无论使用idea中的 单行(–),或 多行注释(/* xx */), 都会报错,idea快捷键
单行: ctrl + /; 多行: ctrl+shift+/.
可以使用标准xml注释语法解决:
语法为: <!-- 你的注释内容 -->

样例如下:

<select id="selectUser" resultType="com.david.mybatis.pojo.User">
        <!-- select * from tb_user where id = #{id} -->
        SELECT
            t.id,
            t.user_name as userName,
            t.`password`,
            t.name,
            <!-- t.age, -->
            t.sex,
            t.birthday,
            t.created,
            t.updated
        FROM
            tb_user AS t
        WHERE
            t.id = #{id}
    </select>

三. # 与 $的区别?
mybatis 的 xxMapper.xml文件中parameterType向sql中传入参数一般有如下2种方式: #{} 和 ${}.
我们推荐任何时候都使用#{}, 一般来说,可以很好的防止sql注入, 因为这种方式的sql语句是经过预编译的,其将被动态解析成一个参数标记符?;
而${}在动态解析的时候,会出入参数实际字符串值.
此外, #将传入的数据都当成一个字符串,会对传入的数据自动加上引号; 将 传 入 的 数 据 直 接 显 示 生 成 在 S Q L 中 。 注 意 : 使 用 将传入的数据直接显示生成在SQL中。注意:使用 SQL使占位符可能会导致SQL注射攻击,能用#的地方就不要使用 , 写 o r d e r b y 子 句 的 时 候 应 该 用 ,写order by子句的时候应该用 orderby而不是#。

综上:

1: #{} 取值为经预编译好sql语句后, 再对相应位置的参数赋值; 故#{}能最大限度防范sql注入攻击; 一般能使用#的方式,就不要使用$.
2: ${} 先取值, 之后再编译sql语句; 无法防止sql注入攻击; $方式一般用于传入数据库对象, 如库名,表名,视图名等固定字符串.

错误案例1:

不恰当的使用 ${}赋值, 及sql语法不严谨导致的错误

配置样例:

<update id="updateUser" parameterType="com.david.mybatis.pojo.User">
        UPDATE tb_user
        <trim prefix="set" suffixOverrides=",">
            <if test="userName!=null">user_name = ${userName},</if>
            <if test="password!=null">password = ${password},</if>
            <if test="name!=null">name = ${name},</if>
            <if test="age!=null">age = ${age},</if>
            <if test="sex!=null">sex = ${sex},</if>
            <if test="birthday!=null">birthday = ${birthday},</if>
            updated = now()
        </trim>
        WHERE
        id = #{id};
    </update>

如案例所示, 使用了不恰当的 传 参 方 式 , 且 u s e r N a m e , p a s s w o r d , n a m e 等 字 符 串 对 象 未 使 用 单 引 号 包 裹 , 使 用 {}传参方式, 且userName,password,name等字符串对象未使用单引号包裹, 使用 ,userName,password,name使,使{}传参,先传入了值, 再编译时, 无法识别不加引号包裹的字符串对象, 导致了如下报错:

解决方案:
使用双引号将变量包裹即可, 如: ‘${userName}’, 仅保证语法正确, 最终还是墙裂推荐使用#{}传参方式.

#运行结果

2020-03-12 13:58:55,311 [main] [org.apache.ibatis.logging.LogFactory]-[DEBUG] Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
2020-03-12 13:58:55,319 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] PooledDataSource forcefully closed/removed all connections.
2020-03-12 13:58:55,319 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] PooledDataSource forcefully closed/removed all connections.
2020-03-12 13:58:55,319 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] PooledDataSource forcefully closed/removed all connections.
2020-03-12 13:58:55,319 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] PooledDataSource forcefully closed/removed all connections.
2020-03-12 13:58:55,427 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
2020-03-12 13:58:55,634 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 624271064.
2020-03-12 13:58:55,634 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@25359ed8]
2020-03-12 13:58:55,641 [main] [UserDao.updateUser]-[DEBUG] ==>  Preparing: UPDATE tb_user set user_name = andy, password = itse3ret, name = 笑笑, age = 2, sex = 1, updated = now() WHERE id = ?; 
2020-03-12 13:58:55,672 [main] [UserDao.updateUser]-[DEBUG] ==> Parameters: 3(String)

org.apache.ibatis.exceptions.PersistenceException: 
### Error updating database.  Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'andy' in 'field list'
### The error may exist in mappers/UserDaoMapper.xml
### The error may involve UserDao.updateUser-Inline
### The error occurred while setting parameters
### SQL: UPDATE tb_user          set user_name = andy,             password = itse3ret,             name = 笑笑,             age = 2,             sex = 1,                          updated = now()          WHERE         id = ?;
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'andy' in 'field list'

	at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:199)
	at com.david.mybatis.dao.impl.UserDaoImpl.updateUser(UserDaoImpl.java:36)
	at com.david.mybatis.dao.UserDaoTest.updateUser(UserDaoTest.java:74)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'andy' in 'field list'
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at com.mysql.jdbc.Util.handleNewInstance(Util.java:408)
	at com.mysql.jdbc.Util.getInstance(Util.java:383)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1062)
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4208)
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4140)
	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2597)
	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2758)
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2826)
	at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2082)
	at com.mysql.jdbc.PreparedStatement.execute(PreparedStatement.java:1302)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.apache.ibatis.logging.jdbc.PreparedStatementLogger.invoke(PreparedStatementLogger.java:59)
	at com.sun.proxy.$Proxy6.execute(Unknown Source)
	at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:47)
	at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:74)
	at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:50)
	at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117)
	at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:197)
	... 25 more

正确案例2:

使用 #{}赋值, 有效防范sql注入攻击, 还可以在 入参数据传入时, 自动进行数据类型识别并填充, 防止如上报错的出现.

配置

运行结果如下:

配置样例:

<update id="updateUser" parameterType="com.david.mybatis.pojo.User">
        UPDATE tb_user
        <trim prefix="set" suffixOverrides=",">
            <if test="userName!=null">user_name = #{userName},</if>
            <if test="password!=null">password = #{password},</if>
            <if test="name!=null">name = #{name},</if>
            <if test="age!=null">age = #{age},</if>
            <if test="sex!=null">sex = #{sex},</if>
            <if test="birthday!=null">birthday = #{birthday},</if>
            updated = now()
        </trim>
        WHERE
          id = #{id};
    </update>

运行结果:

2020-03-12 14:32:20,445 [main] [org.apache.ibatis.logging.LogFactory]-[DEBUG] Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
2020-03-12 14:32:20,455 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] PooledDataSource forcefully closed/removed all connections.
2020-03-12 14:32:20,455 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] PooledDataSource forcefully closed/removed all connections.
2020-03-12 14:32:20,455 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] PooledDataSource forcefully closed/removed all connections.
2020-03-12 14:32:20,455 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] PooledDataSource forcefully closed/removed all connections.
2020-03-12 14:32:20,563 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
2020-03-12 14:32:20,785 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 876926621.
2020-03-12 14:32:20,785 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3444d69d]
2020-03-12 14:32:20,793 [main] [UserDao.updateUser]-[DEBUG] ==>  Preparing: UPDATE tb_user set user_name = ?, password = ?, name = ?, age = ?, sex = ?, updated = now() WHERE id = ?; 
2020-03-12 14:32:20,818 [main] [UserDao.updateUser]-[DEBUG] ==> Parameters: andy(String), 222222(String), 笑笑(String), 2(Integer), 1(Integer), 3(String)
2020-03-12 14:32:20,823 [main] [UserDao.updateUser]-[DEBUG] <==    Updates: 1
2020-03-12 14:32:20,824 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@3444d69d]

参考列表:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值