正文
一,MyBatis的执行流程
mybatis配置文件,包括Mybatis全局配置文件和Mybatis映射文件,其中全局配置文件配置了数据源、事务等信息;映射文件配置了SQL执行相关的
信息。
mybatis通过读取配置文件信息(全局配置文件和映射文件),构造出SqlSessionFactory,即会话工厂。
通过SqlSessionFactory,可以创建SqlSession即会话。Mybatis是通过SqlSession来操作数据库的。
SqlSession本身不能直接操作数据库,它是通过底层的Executor执行器接口来操作数据库的。Executor接口有两个实现类,一个是普通执行器,一个是缓存执行器(默认)。
Executor执行器要处理的SQL信息是封装到一个底层对象MappedStatement中。该对象包括:SQL语句、输入参数映射信息、输出结果集映射信息。其中输入参数和输出结果的映射类型包括java的简单类型、HashMap集合对象、POJO对象类型。
---------------------
第一章. Mybatis 入门
Mybatis是一款优秀的支持自定义SQL查询、存储过程和高级映射的持久化框架,消除了几乎所有的jdbc代码和参数的手动设置以及结果集的检索。
Mybatis可以使用XML或注解进行配置和映射,Mybatis通过将参数映射到配置的SQL形成最终执行的SQL语句,最后将执行SQL结果映射成Java对象返回。
与其他的ORM(对象关系映射)框架不同,Mybatis并没有将Java对象与数据库表关联起来,而是将Java方法与SQL语句关联。
Mybatis允许用户充分利用数据库的各种功能,例如存储过程,视图,各种复杂的查询以及某数据库的专有特性。
如果要对遗留数据库、不规范的数据库进行操作,或者要完全控制SQL的执行,Mybatis将会是一个不错的选择。
Mybatis提供了一个映射引擎,声明式的将SQL语句的执行结果与对象树映射起来。通过使用一种内建的类xml表达式语言,SQL语句可以被动态生成。
Mybatis提供了默认情况下基于Java Hashmap的缓存实现,以及与OScache、Ehcache、Hazelcast和Memcached连接的默认连接器,同时还提供了API供其他缓存实现使用。
第二章. Mybatis XML方式的基本用法
由于Java中的基本数据类型会有默认值,在动态SQL中和null进行比较时结果总为true,所以在实体类中不要使用基本数据类型。
导包(核心,依赖包,数据库驱动包)
有表,有domain
核心配置文件 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>
<!--
每个环境都代表对应一个数据库连接
environments:环境(们)
-->
<!--引入 db.properties文件-->
<properties resource="db.properties" />
<environments default="development">
<!-- 真实数据库环境【用户】 -->
<environment id="development">
<transactionManager type="JDBC" />
<!--dataSource:数据源(连接池) -->
<dataSource type="POOLED">
<!--连接数据库的四大金刚-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 读取SQL的映射文件 -->
<mapper resource="cn/itsource/domain/ProductMapper.xml" />
</mappers>
创建映射文件 XxxMapper.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">
<!--
namespace:命名空间(一个domain对应一个这个xml)
-->
<mapper namespace="cn.itsource.domain.ProductMapper">
<!--
如果到时候想找到这条SQL: namespace+id
等会要找到它:cn.itsource.domain.ProductMapper.findOne
parameterType:代表传参类型
long -> Long _long -> long
resultType:返回的某一个条数的类型(必需写全限定名)
-->
<select id="findOne" parameterType="long" resultType="cn.itsource.domain.Product" >
select * from product where id = #{id}
</select>
</mapper>
以前使用SqlSession通过命名空间调用Mybatis方式时,首先需要用到命名空间和方法id组成的字符串来调用相应的方法。当参数多余1个的时候,需要将所有参数放到一个Map对象中。通过Map传递多个参数,使用起来很不方便,而且还无法避免很多重复的代码。使用接口调用方法就会方便很多,Mybatis使用Java的动态代理可以直接通过接口来调用相应的方法,不需要提供接口的实现类,更不需要在接口类中使用SqlSession以通过命名空间间接调用。另外,当有多个参数的时候,通过参数注解@Param设置参数的名字省去了手动构造Map参数的过程,尤其在Spring中使用的时候,可以配置为自动扫描所有的接口类,直接将接口注入到需要用到的地方。
接口可以配合xml方式使用,也可以配合注解方式来使用。xml方式可以单独使用,但是注解方式必须在接口中使用。
映射XML和接口的命名需要符合如下规则:
1. 当只使用XML而不使用接口的时候,namespace值可以设置为任意不重复的名称。
2. 标签的id属性值在任何时候都不能出现英文".",并且同一个命名空间下不能出现重复的id。
3. 因为接口方法是可以重载的,所以接口中可以出现多个同名但参数不同的方法,但是XML中id的值不能重复,因而接口中的所有同名方法会对应着XML中的同一个id的方法。最常见的用法就是,同名方法中其中一个方法增加一个RowBound类型的参数用于实现分页查询。
2.1. 标签用法
constructor:通过构造方法注入属性的结果值。构造方法中的idArg、arg参数分别对应着id、result标签,它们的含义相同,只是注入方式不同。
resultMap中的id和result标签包含的属性相同。不同的地方在于。id代表的是主键(或唯一值)的字段(可以有多个),他们的属性值是通过setter方法注入的。
property:映射到列结果的属性。可以映射简单的如“username”这样的属性,也可以映射一些复杂对象中的属性,例如“address.number”,这会通过“.”方式的属性嵌套赋值。
javaType:一个Java类的完全限定名,或一个类型别名(通过TypeAlias配置或者默认的类型)。如果映射到一个JavaBean,Mybatis通常可以自动判断属性的类型。如果映射到HashMap,则需要明确的指定javaType的类型(即resultMap的Type属性值设定)。
jdbcType:列对应的数据库类型。JDBC类型仅仅需要对插入,更新,删除操作可能为空的列进行处理。这是JDBC jdbcType的需要,而不是Mybatis的需要。
typeHandler:使用这个属性可以覆盖默认的类型处理器。这个属性值是类的完全限定名或类型别名。
接口中定义的返回值类型必须和XML中配置的resultType类型(或resultMap中的Type)一致,否则就会因为类型不一致而抛出异常。
当返回值最多只有1个结果的时候(可以0个),可以将接口返回值定义为T,而不是List<T>,当然,如果将返回值改为List<T>或T[],也没有问题,只是不建议这么做。刚执行的SQL返回多个结果时,必须使用List<T>或T[]作为返回值,如果使用T,就会抛出TooManyResultsException异常。
可以通过在resultMap中配置property属性和column列的映射,或者在SQL中设置别名这两种方式实现将查询列映射到对象属性的目的。
property属性或别名要和对象中属性的名字相同,但是实际匹配时,Mybatis会先将两者都转为大写形式,然后再判断是否相同。即property=“username”和property=“userName”都可以匹配到对象的userName属性上。判断是否相同的时候要使用USERNAME,因此在设置property属性或别名的时候,不需要考虑大小写是否一致。但是为了便于阅读,要尽可能按照统一的规则来设置。
在数据库中,由于大多数数据库设置不区分大小写,因此下划线方式的命名很常见,如user_name, user_email。在Java中,一般都使用驼峰命名,如userName,userEmail。因为数据库和Java中的这两种命名方式很常见,因此Mybatis还提供了一个全局属性mapUnderscoreToCamelCase, 通过配置这个属性为true可以自动将以下划线方式命名的数据库列映射到Java对象的驼峰式命名属性中。
2.2. 标签用法
parameterType:参数的完全限定类名或别名,属性可选,因为Mybatis可以推断出传入语句的具体参数,因此不建议配置此参数。
flushCache:默认值为true,任何时候只要语句被调用,都会清空一级缓存和二级缓存
timeout:设置在抛出异常前,驱动程序等待数据库返回请求结果的秒数。
useGeneratedKeys:默认值为false。如果设置为true,Mybatis会使用JDBC的getGeneratedKeys方法来取出由数据库内部生成的主键。
keyProperty:Mybatis通过getGeneratedKeys获取主键值后将要赋值的属性名。如果希望得到多个数据库自动生成的列,属性值也可以是以逗号分隔的属性名称列表。
keyColumn:仅对INSERT和UPDATE有用。通过生成的键值设置表中的列名,这个设置仅在某些数据库(如PostgreSQL)中是必须的,当主键列不是表中的第一列时需要设置。如果需要得到多个生成的列,也可以是逗号分隔的属性名称列表。
为了防止类型错误,对于一些特殊的数据类型,建议指定具体的jdbcType值。例如headImg指定BLOB类型,createTime指定TIMESTAMP类型。
由于数据库区分date,time,datetime类型,但是Java中一般都使用java.util.Date类型。因此为了保证数据类型的正确,需要手动指定日期类型。date,time,datetime对应的JDBC类型分别为DATE、TIME、TIMESTAMP。
javaType jdbcType 数据库类型 测试结果
Date Timestamp(不指定默认) datetime 正确
Date DATE datetime 正确
Date TIME datetime 错误
通过上面的测试,说明数据库的datetime类型可以存储DATE(时间部分默认为00:00:00)和TIMESTAMP这两种类型的时间,不能存储TIME类型的时间。将数据库的字段类型修改为time时,就可以正确映射过去了。
2.3. 使用JDBC方式返回主键自增的值
在使用主键自增(如MySQL、SQL Server数据库)时,插入数据库后可能需要得到自增的主键值,然后使用这个值进行一些其他的操作。
<insert id="insert2" useGeneratedKeys="true" keyProperty="id">
insert into sys_user(
user_name, user_password, user_email,
user_info, head_img, create_time)
values(
#{userName}, #{userPassword}, #{userEmail},
#{userInfo}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP})
</insert>
useGeneratedKeys设置为true后,Mybatis会使用JDBC的getGeneratedKeys方法来取出数据库内部生成的主键。获取主键值后将其赋值给keyProperty设置的属性。
当需要设置多个属性时,使用逗号隔开,这种情况下通常还需要设置keyColumn属性,按顺序指定数据库的列,这里列的值会和keyProperty配置的属性一一对应。
2.4. 使用selectKey方式返回主键自增的值
JDBC方式只适用于支持主键自增的数据库。有些数据库(如Oracle)不提供主键自增的功能,而是使用序列得到一个值,然后将这个值赋给id,再将数据插入数据库。对于这种情况,可以采用另外一种方式:使用<selectKey>标签来获取主键的值,这种方式不仅适用于不提供主键自增功能的数据库,也适用于提供主键自增功能的数据库。
<!--MySQL版本-->
<insert id="insert3">
insert into sys_user(
user_name, user_password, user_email,
user_info, head_img, create_time)
values(
#{userName}, #{userPassword}, #{userEmail},
#{userInfo}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP})
<selectKey keyColumn="id" resultType="long" keyProperty="id" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
</insert>
keyColumn、keyProperty:和useGeneratedKeys的用法含义相同
resultType:设置返回值类型
order:和使用的数据库有关。在MySQL中,order设置为AFTER,因为主键值在insert执行成功后才能获取到。而在oracle中,order设置为BEFORE,因为oracle需要先从序列获取值,然后再将值作为主键插入数据库
<!-- Oracle 的例子,查询多个列的时候需要 keyColumn -->
<insert id="insertOracle">
<selectKey keyColumn="id" resultType="long" keyProperty="id" order="BEFORE" databaseId="ORACLE">
SELECT SEQ_USER.nextval from dual
</selectKey>
insert into sys_user(
id, user_name, user_password, user_email,
user_info, head_img, create_time)
values(
#{id}, #{userName}, #{userPassword}, #{userEmail},
#{userInfo}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP})
</insert>
可以发现,selectKey元素放置的位置和之前的MySQL例子中的不同,其实这个元素放置的位置不会影响selectKey中的方法在insert前面或者后面执行的顺序,影响执行顺序的是order属性,这么写仅仅是为了符合实际的执行顺序,看起来更直观而已。
Oracle方式的insert语句中明确写出了id列和值#{id},因为执行selectKey中的语句后id就有值了,我们需要把这个序列值作为主键值插入到数据库中,所以必须指定id列,如果不指定这一列,数据库就会因为主键不能为空而抛出异常。
3.MyBatis的使用细节
1..添加时拿到返回的主键
<!--
parameterType:需要传入我们的对象
useGeneratedKeys: 是否需要主键
keyColumn:主键所在的列,可以不用配置
keyProperty:对象中的属性(代表主键的那个属性)
-->
<insert id="save" parameterType="cn.itsource.domain.Product"
useGeneratedKeys="true"
keyColumn="id"
keyProperty="id"
>
insert into product (productName,dir_id,salePrice,supplier,brand,cutoff,costPrice)
values (#{productName},#{dir_id},#{salePrice},#{supplier},#{brand},#{cutoff},#{costPrice})
</insert>
2.Log4j日志框架
简介:Log4j有三个主要的组件:Loggers(记录器),Appenders (输出源)和Layouts(布局)。可简单理解为日志类别,日志要输出的地方和日志以何种形式输出。综合使用这三个组件可以轻松地记录信息的类型和级别,并可以在运 行时控制日志输出的样式和位置。
使用步鄹:
1.导入日志需要得架包
2.在reource文件夹里面配置log4j.properties文件
![resource文件结构](https://img-blog.csdnimg.cn/20190403184351368.png)
配置说明
log4j.properties(日志文件:)
log4j.rootLogger=ERROR, stdout
#log4j.rootLogger=NONE
log4j.logger.cn.itsource=TRACE //把左边包名改成你自己的包名 TRACE:详细信息
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
3.MyBatis中为一个类取别名
内置别名(文档中有)
自定义别名 mybatis-config.xml\
别名不区别大小写
注意它的配置顺序(如果配置错误会有提示)
<!-- 配置别名 -->
<typeAliases>
<!-- 配置一个别名(不区分大小写) -->
<!--<typeAlias type="cn.itsource.domain.Product" alias="product" />-->
<!--这里是为整个包得类配置别名,他们得别名就是类名,不区分大小写-->
<package name="cn.itsource.domain" />
</typeAliases>
4.列名与属性名不对应的解决方案resultMap
添加与修改在相应位置修改名称即可
查询需要创建一个resultMap
使用的时候返回结果必需是resultMap
<!--
准备一个映射(ORM)配置
resultMap:结果映射
-->
<resultMap id="productMapper" type="product">
<!--如果是主键不一致,使用id这个标签-->
<id column="id" property="id" />
<!--
result:某一个属性的映射
column:列名(表中) property:属性名(对象中)
-->
<result column="dir_id" property="dirId" />
</resultMap>
<!-- 查询所有数据 -->
<select id="findAll" resultMap="productMapper" >
select * from product
</select>
4.常见得几个面试题
①.mybatis相较于jdbc的优点?
把sql语句从java代码中抽取出来,方便维护。并且修改sql时不用修改java代码
不用手动设置参数和对结果集的处理。
②Hibernate与MyBatis的区别(重点)
Hibernate是一个完整的ORM框架,功能更加强大,操作更加简单(性能不好控制),如果你要使用Hibernate控制性能
做一个大项目,Hibernate确实不是很适合,如果大项目一定要使用Hibernate,进行混合使用,一般的操作使用Hibernate,对于性能要求比较高的使用原生的sql.
MyBatis不是完整的ORM框架,需要自己写SQL
好处:自己写SQL,自己控制性能(更好控制)
坏处:自己写SQL,很麻烦
③#与$的区别
#支持传普通参数与对象(map),预编译的方案(性能更高,更加安全,没有SQL注入问题)
$只支持传对象(map),就是一个拼接(性能不好,安全性不高)
能用#都是#,实在不行了再用$