文章目录
Mybatis
环境搭建
单独使用MyBatis
首先说明本文介绍的Mybatis环境搭建是基于javaEE或者javaWeb项目的,并且是单独使用Mybatis框架,以后会有MyBatis与其他框架整合的使用。
-
之前我们创建一个java项目需要导入相应jar包,现在我们使用maven管理的话只需要在pom文件中引入需要的jar包即可。
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency>
2.在java项目的src包下创建MyBatis的配置文件,可以起名为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>
<!-- 对mybatis的相关设置 -->
<settings>
<!-- 开启驼峰映射规则(选配)-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 输出sql语句(选配) -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 全局配置 开启二级缓存,会将项目中所有mapper的缓存全部开启(选配) -->
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- 为实体类映射别名(选配) -->
<typeAliases>
<package name="com.model"/>
</typeAliases>
<!-- 配置对象工厂 -->
<objectFactory type="com.factory.MyFactory">
<property name="stuName" value="张三"/>
<property name="age" value="25"/>
</objectFactory>
<!-- 配置分页插件(选配) -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
<!-- 环境,用来配置数据源,构建SqlSessionFactory -->
<environments default="development">
<!-- 可以配置多个数据源,这代表了一个数据源 -->
<environment id="development">
<!-- 使用哪种方式管理事务 -->
<transactionManager type="JDBC"/>
<!-- 数据源 连接池方式,这个连接池是框架本身的一个实现,不依赖于c3p0这些外部jar包 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/testuseUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--每创建一个mapper文件都需要在这里进行配置,例如下面的例子-->
<mappers>
<mapper resource="com/dao/StuMapper.xml"/>
</mappers>
</configuration>
整合spring+springMVC+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>
<!-- 可以在这里配置一些规则,比如:开启驼峰映射规则,实体类别名,等 -->
<!-- 如果使用MyBatis框架可以将数据源配置在这里 -->
<!-- 使用MyBatis框架每次添加mapper文件需要将mapper的配置sql的xml文件添加在这里 -->
</configuration>
<!--配置文件基本没有必须配置的,因为在需要配置的数据源已经在application-dao.xml文件中配置了,而且还配置了mapper的自动关联-->
<!-- 让spring管理sqlsessionfactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据库连接池 -->
<property name="dataSource" ref="dataSource" />
<!-- 加载mybatis的全局配置文件 -->
<property name="configLocation" value="classpath:mybatis/sqlMapConfig.xml" />
</bean>
<!-- 配置mapper文件和mapper接口的自动关联 -->
<!-- 功能1:mapper文件不需要额外的配置(注册)
功能2:不需要写mapper接口的实现类-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.xrds.mapper" />
</bean>
基础语法
首先mapper.xml文件
namespace属性
namespace 命名空间
解释:
1.命名空间的值要保证唯一性,在一个项目中,每个mapper文件的namespace都不能重复
2.namespace的命名标准,写的是对应接口文件的完全限定名(接口的包名+类名)
3.将一个mapper文件当做一个类,这里面的每一条sql语句,当成类中的一个方法
4.调用mapper文件中语句,namespace的值.sql语句的id值
sql语句中id的属性
id属性:
2.1是该sql语句的唯一标识(在同一个mapper文件中id不能重复)
2.2调用该sql语句的时候需要用到id,可以想象成方法名字
2.3命名标准,一般是跟接口中的方法名一致
sql语句中resultType
resultType属性
3.1返回值类型,需要将查询出的每一条数据映射(转化)为哪一个实体类
3.2无论是查询一条数据还是多条数据(接口方法返回值是实体类还是List集合),resultType写的都是该实体类的完全限定名,不能写List<PhoneModel>
3.3如果要查询多条数据,接口方法,使用List集合接收即可,mybatis会将多条数据,自动存放到list集合里
3.4只有查询可以写resultType,增删改没有该属性
sql语句中parameterType
parameterType属性,在执行语句时需要参数,那么就要写parameterType属性
4.1写的是传递的参数类型,与接口方法中的参数类型一致,例如:传递的参数类型为int 那么就是parameterType="java.lang.Integer",如果传递的参数是一个实体类,那么就写实体类的全路径
4.2一条语句中 只能有一个parameterType属性
4.3获取参数的方式#{} 大括号中写的是参数名字,
如果传递过来的是一个单值类型的参数,那么大括号中写任意字符都可以
如果传递过来的是一个实体类,那么大括号中写的是实体类中的属性名字
注意:
#{}和${}的区别
相当于参数与占位符“?”,用于补全预编译语句,出入数据后用单引号引用,性能较高,可以防止sql注入
什么是sql注入
(1)select * from usename = #{name}
(2)select * from username = ${name}
我们可以输入‘or 1 = 1 ’
sql(1)select * from usename = ‘ 2 or 1 = 1’
sql(2)select * from usename = 2 or 1 =1 所以就会无账户直接登录
例如:
select * from table where id = #{uid}
相当于
select * from table where id = ?
当id = 001 那么
select * from table where id = ‘001’
$ 相当于单纯的字符串的拼接,只有传入数据后才会对sql语句进行编译,执行,所以性能较低
例如:
select * from table name = ${uname}
必须等出入数据才会编译该语句,并进行执行
select * from table name = ‘小明’
一般我们使用#,但是在一些情景下#不适合,就会使用$,
例如:
数据库表名是动态的
select * from #{table}
那么
select * from ‘utable’
但是$
select * from ${table}
select * from utable
sql语句中的resultMap
resultMap属性,可以用于多表关联,比较高级的映射规则
5.1 用于多表联查
5.2 用于映射数据库中所有字段(我们可以将数据库中的字段通过resultMap标签全部映射好,以后的sql可以使用resultMap属性将数据库中的字段与实体类的属性映射,当然我们也可以使用sql标签将所有字段列出,作为公共部分通过include标签引入到sql中)
@Param
可以传递参数,使用@Param注解传递参数,在mapper文件中,语句不需要写parameterType属性
使用@Param注解传递参数时,可以指定参数名字
如果传递的是一个实体类的话,那么就在xml配置文件中获取的是对象的属性{phone.phoneName}
如果参数为实体类的话就直接传递实体,不使用@Param注解,那么在sql中可以直接获取它的属性#{属性名},#{phone}
使用注解@Param() 或者使用mapper属性 parameterType() 都可以
if标签
作用:
1.与java中的if逻辑一致,但没有else结构体
2.if标签可用在一个语句的任意位置
3.test属性,写的判断条件
4.判断时写的是实体类中的某个属性名,或者 map集合中的 某个key,如果使用parameterType传递 了一个单值(int,string)的参数,那么是不能进行if判断的
5.多个判断条件 使用and关键字进行连接
6.获取实体类中的某个属性时,不需要写#{},直接写属性名字
7.如果判断的是级联属性,那么先要判断该级联对象是否为空,然后在判断级联属性,若果不判断级联对象是否为空,那么就会报错
注意:使用if标签时,一定要注意对象不能判断是否为空字符串
比如:Date类型的时间,只能判断是否为null 不能判断是否为 ‘’ (空字符串)
<select id="select" resultType="PhoneModel" parameterType="PhoneModel">
select p.* from phone p left join brand b on p.brand_id = b.id
<where>
<if test="phoneName != null and phoneName != ''">
and phone_name like concat(concat('%',#{moviename},'%'))
</if>
<if test="brandId !=null">
and brand_id = #{brandId}
</if>
<if test="brandModel != null">
<if test="brandModel.brandName != null and brandModel.brandName !=''">
and b.brand_name = #{brandModel.brandName}
</if>
</if>
</where>
</select>
级联查询:
<if test="brandModel != null">
<if test="brandModel.brandName != null and brandModel.brandName !=''">
and b.brand_name = #{brandModel.brandName}
</if>
</if>
模糊查询:
在MyBatis中有四种模糊查询的方式:
方式一:
<select id="searchStudents" resultType="com.example.entity.StudentEntity"
parameterType="com.example.entity.StudentEntity">
SELECT * FROM test_student
<where>
<if test="name != null and name != ''">
AND name LIKE '%${name}%'
</if>
<if test="address != null and address != ''">
AND address LIKE '%${address}%'
</if>
</where>
ORDER BY id
</select>
但是这种方式不能防止sql注入
方式二:
<select id="searchStudents" resultType="com.example.entity.StudentEntity"
parameterType="com.example.entity.StudentEntity">
SELECT * FROM test_student
<where>
<if test="name != null and name != ''">
AND name LIKE "%"#{name}"%"
</if>
<if test="address != null and address != ''">
AND address LIKE "%"#{address}"%"
</if>
</where>
ORDER BY id
</select>
方式三:
<select id="searchStudents" resultType="com.example.entity.StudentEntity"
parameterType="com.example.entity.StudentEntity">
SELECT * FROM test_student
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT(CONCAT('%',#{name},'%'))
</if>
<if test="address != null and address != ''">
AND address LIKE CONCAT(CONCAT('%',#{address},'%'))
</if>
</where>
ORDER BY id
</select>
方式四:
<select id="searchStudents" resultType="com.example.entity.StudentEntity"
parameterType="com.example.entity.StudentEntity">
<bind name="pattern1" value="'%' + _parameter.name + '%'" />
<bind name="pattern2" value="'%' + _parameter.address + '%'" />
SELECT * FROM test_student
<where>
<if test="name != null and name != ''">
AND name LIKE #{pattern1}
</if>
<if test="address != null and address != ''">
AND address LIKE #{pattern2}
</if>
</where>
ORDER BY id
</select>
where标签
作用:
1.动态生成where条件(增删改查都可以使用)
2.会自动生成一个where关键字
3.一般情况下,配合if标签一起使用
4.会将where标签中的第一个and关键字去掉
5.每个条件 都要加上and关键字
6.如果一个判断条件都没通过,那么将不会生成where关键字
set标签
sql 语句更新:
update 表名 set 字段 1= ?,字段2 = ?where 字段3 = ?
作用:
1.只能用于update操作
2.会生成一个set关键字
3.一般情况下,配合if标签来使用
4.会将set标签中 最后一个逗号 去掉
5.where关键字要写在set标签的外面
<update id="update" parameterType="PhoneModel">
update phone
<set>
<if test="phoneName != null and phoneName !=''">
phone_name = #{phoneName},
</if>
<if test="phonePrice != null">
phone_price = #{phonePrice},
</if>
<if test="brandId != null">
brand_id = #{brandId},
</if>
</set>
where id = #{id}
</update>
但是如果所有字段都为null,就会报错的,使用标签可以更新的时候只更新部分字段,其他字段还是原来的数据不进行修改
trim标签
如果能使用where和set标签实现的方法就你不需要trim标签
trim标签:
trim标签
prefix前缀 会在trim标签之前拼接某些字符
prefixOverrides 去掉trim标签中语句第一次出现的关键字
suffix后缀 会在trim标签之后拼接某些字符
suffixOverrides 去掉trim标签中最后一次出现的关键字
注意:在这里所说的前缀和后缀是依据trim标签为参考的,在trim标签之前或者标签之后
1.使用trim实现where标签效果
<select id="selectTrim" parameterType="PhoneModel" resultType="PhoneModel">
select * from phone
<trim prefix="where" prefixOverrides="and">
<if test="phoneName != null and phoneName != ''">
and phone_name like concat (concat('%',#{phoneName},'%'))
</if>
<if test="brandId !=null">
and brand_id = #{brandId}
</if>
</trim>
</select>
2.使用trim标签实现set标签效果
<update id="updateTrim" parameterType="PhoneModel">
update phone
<trim prefix="set" suffix="where" suffixOverrides=",">
<if test="phoneName != null and phoneName !=''">
phone_name = #{phoneName},
</if>
<if test="phonePrice != null">
phone_price = #{phonePrice},
</if>
<if test="brandId != null">
brand_id = #{brandId},
</if>
</trim>
id = #{id}
</update>
............................................................................................................
或者
<update id="updateTrim" parameterType="PhoneModel">
update phone
<trim suffixOverrides=",">
<set>
<if test="phoneName != null and phoneName !=''">
phone_name = #{phoneName},
</if>
<if test="phonePrice != null">
phone_price = #{phonePrice},
</if>
<if test="brandId != null">
brand_id = #{brandId},
</if>
</set>
</trim>
where id = #{id}
</update>
3.使用trim标签实现insert时的校验
<insert id="insertTrim" parameterType="PhoneModel">
<!-- insert into phone(phone_name,phone_price) values (?,?) -->
insert into phone
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="phoneName !=null and phoneName !=''">
phone_name,
</if>
<if test="phonePrice != null">
phone_price,
</if>
<if test="brandId != null">
brand_id,
</if>
</trim>
values
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="phoneName !=null and phoneName !=''">
#{phoneName},
</if>
<if test="phonePrice != null">
#{phonePrice},
</if>
<if test="brandId != null">
#{brandId},
</if>
</trim>
</insert>
choose标签
作用:
1.跟java中的if- else if - else 的逻辑是一样的
2.第一个when代表了if
其他的when代表了else-if
otherwise代表了else
<select id="selectChoose" parameterType="PhoneModel" resultType="PhoneModel">
select * from phone
<where>
<choose>
<when test="brandId != null">
and brand_id = #{brandId}
</when>
<when test="id != null">
and id = #{id}
</when>
<otherwise>
and 1=1
</otherwise>
</choose>
</where>
</select>
bind标签
常用与模糊查询,构建sql 语句
作用:
1.可以构建一段sql语句
2.不能使用parameterType,要使用@Param注解来传递参数
3.如果传递参数时没有指定参数名字,那么要使用_parameter来获取参数
(1)
<select id="selectBind" resultType="PhoneModel">
<!-- %值% -->
<bind name="testSql" value=" '%'+ phoneName +'%' "/>
select * from phone where phone_name like #{testSql}
</select>
(2)
<select id="searchStudents" resultType="com.example.entity.StudentEntity"
parameterType="com.example.entity.StudentEntity">
<bind name="pattern1" value="'%' + **_parameter.name** + '%'" />
<bind name="pattern2" value="'%' + **_parameter.address** + '%'" />
SELECT * FROM test_student
<where>
<if test="age != null and age != '' and compare != null and compare != ''">
age
${compare}
#{age}
</if>
<if test="name != null and name != ''">
AND name LIKE #{pattern1}
</if>
<if test="address != null and address != ''">
AND address LIKE #{pattern2}
</if>
</where>
ORDER BY id
</select>
foreach标签
场景:
1.向一张表中插入多条数据
用法:
1.与c:forEach标签 进行对比 var=“i” items="${list}"
2.collection属性 需要循环的集合,与items属性类似
3.item属性 跟c标签中的var是一样的
4.separator属性 每次循环后都会拼接相应的字符,最后一次循环不会拼接
5.open属性 在第一次循环开始之前拼接某些字符
6.close属性 在循环结束之后拼接某些字符
<insert id="insertForeach" parameterType="list">
insert into brand(brand_name)
values
<foreach collection="list" item="i" separator=",">
(#{i.brandName})
</foreach>
</insert>
2.使用foreach进行where条件的拼接
<!--
使用@Param注解传递两个参数
第一个价格 phonePrice
第二个是名字 是一个集合 nameList List<PhoneModel>
-->
<select id="selectForeach" resultType="PhoneModel">
select * from phone
where phone_price > #{phonePrice}
<foreach collection="nameList" item="i" open="and (" close=")" separator="or">
phone_name like concat(concat('%',#{i.phoneName},'%'))
</foreach>
</select>
resultMap标签
属性:
id用于表示resultMap
type用于用于指定此resultMap所映射的实体类的完全限定名(类名+包名)
用法:
1.resultMap也能用于设置返回值的类型,只能用于查询
2.在同一个语句中,resultType和resultMap只能出现一个
3.resultMap写的是一个resultMap标签的id值
4.如果只是单表查询,那么使用resultType即可,如果是多表连接,并且需要对结果集进行一对多或者一对一的映射,那么则使用resultMap
5.type属性,查询是谁是主表,那么就写和主表对应的那一个实体类
6.resultMap可以将字段映射作为公共部分用于后续的sql语句中
标签:
1.id标签,用来映射主表的主键字段,只能出现一次,而且必须要写
如果是用于设置公共的字段,那么id映射的是数据表的主键id
column属性:代表了某一个字段名,一定要根据查询后的字段名字来映射,也可以是别名(也就是数据库表中字段的名称,或者映射的别名)
property属性:代表了实体类中某一个属性
javaType属性(可省略):代表了property属性所指定的 变量在实体类中是什么类型的,(副表的返回值映射的实体类)
jdbcType属性(可省略):代表了column属性所指定的字段在表中是什么类型的(一般我们都需要写此属性,用于指定字段类型)
2.result标签,用来映射除主键以外的其他普通字段,每个字段对应一个result标签,与id标签规则一致
autoMapping属性:如果查询后的列名和属性名一致,或者开启了驼峰映射,那么不需要写result标签,只需要将resultMap标签的autoMapping属性设置为true即可(默认为false,即默认不进行驼峰映射)
也可以结合result将不一样的或者不满足驼峰规则的单独进行映射规则
3.association标签 用于做一对一的映射(实体类中有一个其他的实体类属性)(为一个级联属性进行映射)
property属性:实体类中的某一个属性,要为哪个属性进行映射,就写哪个属性的属性名
4.collection标签 一对多的映射
property属性:list集合在实体类中的属性名
ofType属性:list集合中实体类的类型(泛型类型)
如果一对一查询,也就是一个实体类对应另一个实体类查询
例如:
(1)一个手机对应一个品牌,那么在创建实体类的时候就是一个手机和一个品牌的实体类
查询sql如下
说明:Mybaits的配置文件中设置了驼峰映射,并且通过typeAliases标签这是了实体类的别名,所以resultMap的type属性可以直接写实体类的类名,并且设置autoMapping属性为true,如果没有设置驼峰映射,以及实体类别名,那么就可以将type属性设置为实体类的完全限定名,并且不需要设置autoMapping属性
<resultMap type="PhoneModel" id="phoneMap" autoMapping="true">
<id column="id" property="id"/>
说明:因为设置了驼峰映射并且autoMapping设置为true,那么可以只写id标签,省略result标签
<!-- 一对一 -->
<association property="brandModel" javaType="BrandModel" autoMapping="true">
<id column="b_id" property="id"/>
</association>
</resultMap>
<select id="select" resultMap="phoneMap">
select *,b.id b_id from phone p
left join brand b
on p.brand_id = b.id
</select>
注意:两个表中的有相同的字段,需要将相同的字段进行区分,首先在查询语句中将
相同的字段单独查询出来并起别名,在将别名填在association标签中的id标签的column属性中,这时候查询出来的才是正确的
(2)如果查询一个品牌的手机那么就是一对多映射,一个品牌对应多个手机
<resultMap type="BrandModel" id="brandMap" autoMapping="true">
<id column="id" property="id"/>
<!-- 一对多映射 -->
<collection property="phoneList" ofType="PhoneModel" autoMapping="true">
<id column="p_id" property="id"/>
</collection>
</resultMap>
<select id="selectBrand" resultMap="brandMap">
select *,p.id p_id from brand b
left join phone p
on p.brand_id = b.id
</select>
(3)如果只是 单纯了查询一个表单
<resultMap id="BaseResultMap" type="com.thunisoft.consistency.bean.DataSourceResultBean">
<id column="c_bh" property="cBh" jdbcType="CHAR"/>
<result column="c_ywbs" property="cYwbs" jdbcType="VARCHAR"/>
<result column="c_maintask_bh" property="cMaintaskBh" jdbcType="CHAR"/>
<result column="c_business_id" property="cBusinessId" jdbcType="CHAR"/>
<result column="c_data" property="cData" jdbcType="VARCHAR"/>
<result column="c_data_format" property="cDataFormat" jdbcType="VARCHAR"/>
<result column="n_datasource_serial_number" property="nDatasourceSerialNumber" jdbcType="INTEGER"/>
<result column="n_modifytype" property="nModifyType" jdbcType="INTEGER"/>
<result column="dt_createtime" property="dtCreatetime" jdbcType="TIMESTAMP"/>
<result column="dt_modifytime" property="dtModifytime" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">
c_bh,c_ywbs,c_maintask_bh,c_business_id,c_data,c_data_format,n_datasource_serial_number,n_modifytype,dt_createtime,dt_modifytime
</sql>
<select id="selectDataSourceResult" resultMap="BaseResultMap" >
select
<include refid="Base_Column_List"/>
from db_consistency.t_rcp_datasource_result where
c_ywbs = #{cYwbs}
and
c_maintask_bh = #{cMaintaskBh}
and
c_business_id = #{cBusinessId}
</select>
sql标签
通过sql标签达到代码的复用
嵌套查询
嵌套查询实现一对一映射
嵌套查询 select属性(出现在一对一或者一对多的标签中)
用法:
1.只能在resultMap中使用
2.指定了select属性后,会自动调用另外一条sql语句
3.select所写的是另外一条sql语句的id,如果调用的是当前mapper文件的sql语句,那么直接写语句的id,如果调用的是
其他mapper文件中的sql语句,需要使用namespace.id的形式调用
4.调用了语句后,会将该语句的映射结果 赋值给property属性所指定的值
5.column属性,写的时查询后的某一个字段名字,作用是将该字段的值传递到下一个语句中
6.fetchType属性为 是否延迟加载。默认是立即加载,设置为lazy为延迟加载(只有在嵌套查询时才会生效)
7.传递多个参数{brandId=brand_id,phoneName=phone_name} 等号左侧是自定义的参数名,右侧是查询出的数据中某一个字段的名字,在第二条语句中,使用传递过来的参数名获取参数值,如果传递多个参数,第二条语句,不要写parameterType属性
<resultMap type="PhoneModel" id="pMap">
<id column="id" property="id"/>
<!-- 使用嵌套查询进行一对一的映射 -->
<association property="brandModel" javaType="BrandModel" select="selectBrandById" column="brand_id" fetchType="lazy">
</association>
</resultMap>
<select id="selectPhone" resultMap="pMap">
select * from phone
</select>
<select id="selectBrandById" resultType="BrandModel" parameterType="int">
select * from brand where id = #{id}
</select>
嵌套查询实现一对多映射
<!-- 嵌套查询实现一对多 -->
<resultMap type="BrandModel" id="bMap">
<id column="id" property="id"/>
<result property="brandName" column="brand_name" jdbcType="VARCHAR" typeHandler="com.typeHandler.MyTypeHandler"/>
<collection property="phoneList" ofType="PhoneModel" select="selectPhoneByBrandId" column="id" >
</collection>
</resultMap>
<select id="selectAllBrand" resultMap="bMap">
select * from brand
</select>
<select id="selectPhoneByBrandId" resultType="PhoneModel" parameterType="int">
select * from phone where brand_id = #{id}
</select>
鉴别器
场景:
1.创建手机的基础类
2.创建两个子类,分别是苹果手机和安卓手机
描述:
如果购买了苹果手机,那么将赠送手机壳一个
如果购买了安卓手机,那么将赠送充电宝一个
在结果映射的时候,通过brand_id进行判断,是苹果手机还是安卓,然后将数据映射为不同的实体类
discriminator标签 鉴别器
用法:
1.resultMap标签的type属性返回的还是跟主表对应的实体类
2.鉴别器所鉴别的类,要继承基础类
3.鉴别器标签column属性:代表了表中的一个字段,意思是要对这个字段的值 进行判断
4.鉴别器标签javaType属性:代表了column指定的字段所对应的java类型
5.判断时,使用case子标签进行判断,value指的是字段的值,resultType指的是相关映射规则,也就是继承实体类的那个实体类
type:返回值实体类
id:此resultMap的唯一标识
<resultMap type="PhoneModel" id="disMap">
<!--id标签:主表的主键字段-->
<id column="id" property="id"/>
<!-- 鉴别器 -->
<!--column:返回值实体类的属性,通过该属性来判断手机类型 javaType:返回值实体类的属性的java类型-->
<discriminator javaType="int" column="brand_id">
<!--case标签:value属性:上述column的值,也就是根据该具体值来判断手机类型
resultType:表示当value值为几的时候,对应的手机类型的实体类-->
<case value="1" resultType="ApplePhone">
<!--property:该属性是上述resultType的实体类的属性,
column:表示在查询到的表中的字段,也就是当value为1的时候对应的是实体类resultType的ApplePhone的属性shouJiKe对应的在查询到的表中具体的数据—手机壳-->
<result property="shouJiKe" column="shouJiKe"/>
</case>
<case value="2" resultType="AndRoidPhone">
<result property="chongDianBao" column="chongDianBao"/>
</case>
<case value="3" resultType="AndRoidPhone">
<result property="chongDianBao" column="chongDianBao"/>
</case>
</discriminator>
</resultMap>
<select id="selectDiscr" resultMap="disMap">
select *, '手机壳' as shouJiKe, '充电宝' as chongDianBao from phone
</select>
分页插件
使用pageHelper插件
1.导入两个jar包
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.4</version>
</dependency>
2.在MyBatis主配置文件中配置
<!-- 配置分页插件 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
如果需要分页,在调用select方法前调用,表示显示第一页的且每页5条数据
//pageNum表示第几页,pageSize表示每页多少条数据
Page<Object> page = PageHelper.startPage(int pageNum, int pageSize) //出现在此代码后第一个查询语句会被分页
//还有一个方法,多了一个参数count,当count为true是表示查询数据总量
Page<Object> page = PageHelper.startPage(int pageNum, int pageSize, boolean count)
//通过此方法可以获取到数据总量
long total = page.getTotal();
//若需要需要分页的详细数据可以通过将上述page存入PageInfo中,然后获取
PageInfo<Object> pageInfo = new PageInfo<>(page);
//比如:提供下列方法
pageInfo.getTotal();
pageInfo.getEndRow();
pageInfo.getStartRow();
pageInfo.getPages();
......
其他
解决数据库字段和实体类的属性不一致问题
1.在查询时,将不一样的列名起一个别名,让他跟属性名一致
2.在MyBatis配置文件中开启驼峰映射
例如:
<settings>
<!-- 开启驼峰映射规则-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 打印sql语句 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
为实体类映射别名
<!-- 为实体类映射别名 -->
<typeAliases>
<!--
为一个包下的所有类进行别名的映射
name 是 一个包的路径
映射后的别名是 类名(不区分大小写)
做完了别名映射后,在该项目的所有mapper文件中,只要使用到了该实体类,那么不需要写完全限定名了,直接写类名即可
(resultType, parameterType这两个属性可能用到实体类的完全限定名即返回值和参数)
如果写了完全限定名,也是没错的
-->
<package name=*"com.model"*/>
<!--
typeAlias 为一个类进行别名的映射
type 是该类的完全限定名字
alias 是要映射的别名
-->
<!-- <typeAlias type="com.model.PhoneModel" alias="phone"/> -->
</typeAliases>
@Alias注解
需要写在类定义处
首先需要保证配置了 <package name=“com.model”/>
如果在扫描时发现该类带有@Alias那么将使用注解所指定的值 当做别名
如果没有注解,那么则使用类名当做别名
面向接口的编程思想
1.Mapper文件的文件名称 与 接口名 对应
2.Mapper文件的namespace属性值 与 对应接口的完全限定名一致
3.Mapper的sql中id 名 与接口的方法名一致
4.Mapper的sql返回值类型resultType 与 接口的返回值一致,Mapper的返回值是完全限定名
5.Mapper的sql参数类型parameterType 与 接口的参数类型一致
缓存
一级缓存
1.一级缓存(小缓存)默认开启 – HashMap
一级缓存(local cache), 即本地缓存, 作用域默认为sqlSession。当Session flush或close 后,该Session中的所有Cache将被清空,或者执行增删改操作都会将缓存清空。 本地缓存不能被关闭, 但可以调用clearCache() 来清空本地缓存, 或者改变缓存的作用域。在mybatis3.1之后, 可以配置本地缓存的作用域,在mybatis.xml 中配置 。同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中,key:hashCode+查询的SqlId+编写的sql查询语句+参数
(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认为,对于两次查询,如果以下条件都完全一样,那么就认为它们是完全相同的两次查询。
a、 传入的statementId
b、 查询时要求的结果集中的结果范围
c、 这次查询所产生的最终要传递给JDBC java.sql.Preparedstatement的Sql语句字符串(boundSql.getSql() )
d、 传递给java.sql.Statement要设置的参数值
二级缓存
2.二级缓存(大缓存)默认不开启
基于namespace,每个mapper文件有自己的一个独立缓存
(1).要保证SqlSessionFactory是唯一的
(2).查询完之后,如果想让结果缓存,那么需要手动调用commit方法
(3).相关的实体类必须实现序列化接口即实现Serializable接口
若配置了二级缓存意味着:
(1).映射语句文件中的所有select语句将会被缓存。
(2).映射语句文件中的所欲insert、update和delete语句会刷新缓存。
(3).缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法来收回。
(4).根据时间表,比如No Flush Interval,(CNFI没有刷新间隔),缓存不会以任何时间顺序来刷新。
(5).缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用
(6).缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全的被调用者修改,不干扰其他调用者或线程所做的潜在修改。
在Mybatis-config.xml中开启二级缓存
<configuration>
<settings>
<!--这个配置使全局的映射器(二级缓存)启用或禁用缓存-->
<setting name="cacheEnabled" value="true" />
</settings>
</configuration>
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="com.yihaomen.mybatis.dao.StudentMapper">
<!--开启本mapper的namespace下的二级缓存-->
<!--
eviction:缓存的回收策略:
(1)LRU – 最近最少使用的:移除最长时间不被使用的对象。
(2)FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
(3)SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
(4)WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是 LRU。
flushInterval:缓存刷新间隔
缓存多长时间清空一次,默认不清空,设置一个毫秒值
readOnly:是否只读:
true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。
mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快
false:非只读:mybatis觉得获取的数据可能会被修改。
mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢
size:缓存存放多少元素;
type="":指定自定义缓存的全类名: <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
实现Cache接口即可;
blocking: 若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存。
-->
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>
<!--可以通过设置useCache来规定这个sql是否开启缓存,ture是开启,false是关闭-->
<select id="selectAllStudents" resultMap="studentMap" useCache="true">
SELECT id, name, age FROM student
</select>
<!--刷新二级缓存-->
<select id="selectAllStudents" resultMap="studentMap" flushCache="true">
SELECT id, name, age FROM student
</select>
</mapper>
其他配置说明
和缓存有关的设置/属性:
1)、cacheEnabled=true(false):开启二级缓存(关闭二级缓存)(一级缓存一直可用的)
2)、每个select标签都有useCache=true(false):使用缓存(一级缓存依然使用,二级缓存不使用)在全局开启的情况下可以禁止部分查询使用二级缓存
3)、每个增删改标签的:flushCache="true":(一级二级都会清除)增删改执行完成后就会清除缓存;
4)、sqlSession.clearCache();只是清除当前session的一级缓存;
5)、localCacheScope:本地缓存作用域:(一级缓存SESSION);当前会话的所有数据保存在会话缓存中;
STATEMENT:可以禁用一级缓存;
MyBatis原理
1.接口层 SqlSession
2.核心处理层(6):配置解析,参数映射,SQL解析,SQL执行,结果集映射,插件
3.基础支持层(9):数据源模块,事务管理模块,缓存模块,Bingding模块,反射模块—反射工厂,类型转换,日志模块,资源加载,解析器模块
MyBatis的主要成员
- Configuration MyBatis所有的配置信息都保存在Configuration对象之中,配置文件中的大部分配置都会存储到该类中
- SqlSession 作为MyBatis工作的主要顶层API,表示和数据库交互时的会话,完成必要数据库增删改查功能
- Executor MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
- StatementHandler 封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数等
- ParameterHandler 负责对用户传递的参数转换成JDBC Statement 所对应的数据类型
- ResultSetHandler 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合
- TypeHandler 负责java数据类型和jdbc数据类型(也可以说是数据表列类型)之间的映射和转换
- MappedStatement MappedStatement维护一条<select|update|delete|insert>节点的封装
- SqlSource 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
- BoundSql 表示动态生成的SQL语句以及相应的参数信息
Mybatis的层次
Mybatis实现过程
应用层会读取配置,有多种方式进行配置:基于XML配置,基于注解配置,基于Java API方法配置等,创建sql语句
然后构建SqlSessionFactory,SqlSessionFactory是一个接口,提供了一个默认的实现类DefaultSqlSessionFactory,
通过SqlSessionFactory.openSession(),获取到SqlSession,然后开始数据处理,如层次图中数据处理层的顺序,参数映射使用到了ParameterHandler ,SQL解析使用SqlSource,Sql执行通过执行器Executor,结果处理映射ResultSetHandler,最后将结果返回