1.全局配置
<properties></properties>引入外部*.ptoperties文件,可以通过${jdbc.ulr}区出properties中的值
<settings>
<setting name="" value=""/>定义mybatis的一些全局性设置,在官网可以查看具体设置哪些
<setting name="mapUnderscoreToCamelCase" value="false"/>设置是否开启下划线命名法转驼峰命名法
</settings>
<typeAliases></typeAliases> 为一些类定义别名
<typeHandlers></typeHandlers> 类型处理器:定义Java类型与数据库中的数据类型之间的转换关系
<objectFactory type=""></objectFactory> 对象工厂
<plugins> 插件:mybatis的插件,插件可以修改mybatis的内部运行规则
<plugin interceptor=""></plugin>
</plugins>
2.mapper映射文件
-
增删改查
-
<mapper namespace="com.njupt.mapper.StudentMapper"> <!-- 接口全类名 --><!-- resultType 可以省略 --><select id="getStudentById" resultType="com.njupt.entities.Student">select * from student where id=#{id}</select><!-- resultType 可以省略 --><insert id="addStudent" parameterType="com.njupt.entities.Student">insert into student(name,age) values(#{name},#{age})</insert><!-- resultType 可以省略 --><update id="updateStudent" parameterType="com.njupt.entities.Student">update student set name=#{name}, age=#{age} where id = #{id}</update><!-- resultType 可以省略 --><delete id="deleteStudent">delete from student where id=#{id}</delete></mapper>
-
使用方法
-
public void getStudentById() throws IOException{String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);//1.获取sqlSessionFactory对象SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//2.获取SqlSession对象那个SqlSession session = sqlSessionFactory.openSession();//会为接口自动创建一个代理对象,代理对象执行对应的方法StudentMapper studentMapper = session.getMapper(StudentMapper.class);Student student = studentMapper.getStudentById(1);System.out.println(student);int tmp = studentMapper.addStudent(new Student("aaa",18));session.commit();}
-
增删改的注意事项
-
sqlSessionFactory.openSession()这种方式不会自动提交修改,sqlSessionFactory.openSession(true)这样会自动提交
-
增删改标签没有resultType标签,但是会返回操作的数据行数。可以自动转为int,long,boolean类型
-
-
添加并获取自增主键
-
useGeneratedKeys表示使用自增主键,keyProperty表示将使用jDBC操作获取的id赋值给参数的哪个属性<insert id="addStudent" parameterType="com.njupt.entities.Student" useGeneratedKeys="true" keyProperty="id">insert into student(name,age) values(#{name},#{age})</insert>String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession session = sqlSessionFactory.openSession();StudentMapper studentMapper = session.getMapper(StudentMapper.class);Student student = new Student("bbb",18);//此时student的id是没有值的int tmp = studentMapper.addStudent(student);System.out.println(student.getId()); //输出3session.commit();System.out.println(student.getId());//输出3
3.Mybatis参数处理
-
只有一个参数时,Mybatis不做处理,随便取,都可以取出来
-
public Student getStudentById(Integer id);
-
<select id="getStudentById" resultType="com.njupt.entities.Student">
-
select * from student where id=#{id}//这里的#{}可以随便写,#{a},#{b}
-
</select>
-
-
当有多个参数时,Mybati会将多个参数封装成map
-
key为param1,param2.......paramN; value为参数值
-
或者采用#{0},#{1}这种根据索引取值
-
使用@param注解指定key
-
public Student getStudentByIdAndName(@Param("id")int id, @Param("name")String name);
-
<select id="getStudentByIdAndName" resultType="com.njupt.entities.Student">
-
select * from student where id=#{id} and name=#{name}
-
</select>
-
-
直接使用POJO对象
-
public int addStudent(Student student);
-
-
如果多个参数不是实体类,但是又经常使用,那么就自定义一个参数实体类,专门用来传参
-
将多个参数封装成map,直接传入自定义的map
-
-
当传入的参数是Collection(List,Set)的对象时,也特殊处理
-
Collection=====>#{collection}
-
List=====>#{list[0]},List的默认键为list
-
Set======>#{set}
-
public Student getStudentByIds(List<Integer> ids);
-
<select id="getStudentByIds" resultType="com.njupt.entities.Student">
-
select * from student where id=#{list[0]}
-
</select>
-
-
-
示例,考虑以下场景如何取值
-
public Student getStudentByIdAndName(@Param("id")int id,String name);
-
id==>#{id/param1} name==>#{param2}
-
-
public Student getStudentByIdAndName(@Param("id")int id,Student student);
-
id==>#{id/param1}, name==>#{param2.name}
-
-
-
#{}与${}都可以取出参数的值,他们有什么区别
-
#{}:是以预编译的形式,将参数设置到sql语句中,PreparedStatement; 可以防止sql注入
-
${}:取出的值直接拼接在sql语句中
-
什么情况下使用#{}与${}
-
只要支持PreparedStatement的,都采用#{}
-
不支持PreparedStatement都采用${},例如,表名,排序等
-
select * from 2016_table/2017_table/2018_table -->select *from table ${year}_table
-
select * from table order by ${f_name} ${order}
-
-
-
-
#{}:还可以规定参数的一些规则
-
javaType, jdbcType, mode(存储过程),numericScale(保留小数位数),resultMap, typeHandler,jdbcTypeName,expression
-
javaType通常在某些特定条件下需要被设置
-
假设添加email字段为null,java类型:null----映射---->jdbc类型:OTHER。而oracle不支持OTHER类型就会报错
-
解决方案一:修改全局配置
-
<setting name="jdbcTypeForNull" value="NULL"/>
-
-
解决方案二:
-
-
-
#{email,jdbcType=NULL}
-
-
4.查询语句的细节
-
查询语句返回一个List那么ResultType应该放的是List中元素的类型
-
public List<Student> getStudentsGreaterThanAge(@Param("age") int age);
-
<select id="getStudentsGreaterThanAge" resultType="com.njupt.entities.Student">
-
select * from student where age>=#{age}
-
</select>
-
-
返回map
-
返回一个map,键为数据库的列名,值为数据库的值
-
public Map<String,Object> getMapById(@Param("age") int id);
-
<select id="getMapById" resultType="map">
-
select * from student where id=#{id}
-
</select>
-
-
返回一个map,键为指定的键,值为POJO对象
-
@MapKey("id")
-
public Map<Integer, Student> getStudentsByAge(@Param("age") int age);
-
<select id="getStudentsByAge" resultType="com.njupt.entities.Student">
-
select * from student where age>=#{age}
-
</select>
-
-
如果数据库列名与javaBean属性字段不匹配,返回结果无法封装成javaBean,该怎么办
-
给列名起别名
-
满足驼峰命名法,我们在全局配置文件中开启驼峰命名法
-
使用resultMap自定义映射
-
<!-- type表示封装的javaBean类型,id为这个映射规则返回的Map类型的别名 -->
-
<resultMap type="com.njupt.entities.Student" id="stu">
-
<!-- 元素id表示主键映射,coulumn表示数据库的哪一列,property表示映射为javaBean的哪个属性 -->
-
<id column="id" property="id"/>
-
<!-- 元素result表示普通列的映射,coulumn表示数据库的哪一列,property表示映射为javaBean的哪个属性 -->
-
<result column="name" property="name"/>
-
</resultMap>
-
<select id="getStudentsByAge" resultMap="stu">
-
select * from student where age>=#{age}
-
</select>
-
-
-
-
resultMap的高级用法
-
一对一,如果javaBean对象内的属性也有一个对象该怎么做呢,例如学生信息里有学校
-
联合查询
-
级联属性
-
<resultMap type="com.njupt.entities.Student" id="stuPlus">
-
<id column="id" property="id"/>
-
<result column="name" property="name"/>
-
<result column="scId" property="school.id"/>
-
<result column="scName" property="school.name"/>
-
</resultMap>
-
<select id="getStudentById" resultMap="stuPlus">
-
select st.id id,st.name name, st.sc_id schoolId, sc.id scId, sc.name scName
-
from student st
-
inner join school sc
-
on st.sc_id=sc.id
-
where id=#{id}
-
</select>
-
-
利用association标签实现属性对象的封装
-
<resultMap type="com.njupt.entities.Student" id="stuPlus">
-
<id column="id" property="id"/>
-
<result column="name" property="name"/>
-
<association property="school" javaType="com.njupt.entities.School">
-
<id column="scId" property="id"/>
-
<result column="scName" property="name"/>
-
</association>
-
</resultMap>
-
-
-
分步查询association
-
学生内有一个属性时学校,我们可以先查出学生信息,然后根据查出的学生信息的scId,再去查学校信息
-
-
-
-
一对多,如果一个学校含有很多学生,该怎么查呢 collection
-
联合查询
-
<resultMap type="com.njupt.entities.School" id="sch">
-
<id column="id" property="id"/>
-
<result column="name" property="name"/>
-
<collection property="stuList" ofType="com.njupt.entities.Student">
-
<id column="stId" property="id"/>
-
<result column="stName" property="name"/>
-
</collection>
-
</resultMap>
-
<select id="getAllStudentsInSchool" resultMap="sch">
-
select sc.id id,sc.name name, st.id stId, st.name stName
-
from school sc
-
inner join student st
-
on sc.id=st.sc_id
-
where id=#{id}
-
</select>
-
-
分步查询
-
<resultMap type="com.njupt.entities.School" id="sch">
-
<id column="id" property="id"/>
-
<result column="name" property="name"/>
-
<collection property="stuList" select="com.njupt.mapper.StudentMapper.getStudentsByScId" column="id">
-
</collection>
-
</resultMap>
-
<select id="getAllStudentsInSchool" resultMap="sch">
-
select id, name
-
from school
-
where id=#{id}
-
</select>
-
-
-
扩展
-
在上面的示例中,第二步查询只传递了一个参数,如果有多个参数怎么办column="{id=id,name=name}"
-
<collection property="stuList" select="com.njupt.mapper.StudentMapper.getStudentsByScId" column="{id=id,name=name}" >
-
-
-
-
延迟加载,考虑到association每次都会发送两条sql,当我们调用某个字段时,才去发送对应的sql,我们需要开启延迟加载
-
全局配置
-
<setting name="lazyLoadingEnabled" value="true"/>开启延迟加载
-
<setting name="aggressivelazyLoadingEnabled" value="false"/>关闭非延迟加载
-
-
分步查询的标签有个字段fetchType也可设置延迟加载,且其优先级高于全局配置
-
<collection property="stuList" select="com.njupt.mapper.StudentMapper.getStudentsByScId" column="id" fetchType="lazy">
-
-
5.动态sql
-
OGNL表达式(Apache开源项目) http://commons.apache.org/proper/commons-ognl/language-guide.html
-
-
if标签
-
要求根据传过来的参数,字段非空,来设置sql语句
-
<select id="getStudentByIf" resultType="com.njupt.entities.Student">
-
select * from student where
-
<if test="id!=null">id=#{id}</if>
-
<if test="name!=null and name!='' ">and name=#{name}</if>
-
</select>
-
OGNL虽然支持&& 等逻辑判断,但是由于在xml中&属于特殊字符,需要使用html的实体字符来替代
-
-
-
where标签,用于查询时去除头部多余的and
-
对于上面的sql拼装,我们发现如果id为空,那么sql语句就变为了。select * from student where and name=#{name}这就错了
-
where标签可以去除头部的and或者or等
-
<select id="getStudentByIf" resultType="com.njupt.entities.Student">
-
select * from student
-
<where>
-
<if test="id!=null">id=#{id}</if>
-
<if test="name!=null and name!='' ">and name=#{name}</if>
-
</where>
-
</select>
-
-
-
trim标签
-
prefix:给trim标签体最终返回的结果,加一个前缀
-
prefixOverrides:给trim标签体最终返回的结果,删除指定前缀
-
suffix:给trim标签体最终返回的结果,加一个后缀
-
suffixOverrides:给trim标签体最终返回的结果,删除指定后缀
-
<select id="getStudentByIf" resultType="com.njupt.entities.Student">
-
select * from student
-
<trim prefix="where" prefixOverrides="" suffix="" suffixOverrides="and">
-
<if test="id!=null">id=#{id} and</if>
-
<if test="name!=null and name!='' "> name=#{name}</if>
-
</trim>
-
</select>
-
-
choose标签,分支选择,相当于带了break的switch-case语句
-
进行一个查询,带了id字段就用id查询,带了name字段就用name查询
-
<select id="getStudentByIf" resultType="com.njupt.entities.Student">
-
select * from student
-
<where>
-
<choose>
-
<when test="id!=null"> id=#{id}</when>
-
<when test="name!=null">name=#{name}</when>
-
<otherwise>1=1</otherwise>
-
</choose>
-
</where>
-
</select>
-
-
set标签,用于去除更新操作时,多余的逗号","
-
<update id="updateStudent">
-
update student
-
<set>
-
<if test="name!=null">name=#{name},</if>
-
<if test="age!=null">age=#{age}</if>
-
</set>
-
<where>
-
id=#{id}
-
</where>
-
</update>
-
-
foreach标签,用于批量操作
-
批量删除,方法1,
-
public int deleteMoreStu(String ids);
-
<delete id="deleteMoreStu">
-
delete from student where id in (${ids})
-
</delete>
-
studentMapper.deleteMoreStu("1,2,3,4,5");
-
-
利用foreach批量处理集合,
-
collection:集合名
-
item:集合内的每个元素的临时名称
-
open:在循环结束后得到的结果前面,添加的字符串
-
close:在循环结束后得到的结果后面,添加的字符串
-
index:如果Collection为List,则index为下标。如果为Map,则index为键
-
separator:分隔符
-
public int deleteMoreStu(@Param("ids")List<Integer> ids);
-
<delete id="deleteMoreStu">
-
delete from student where id in
-
<foreach collection="ids" item="id" open="(" close=")" separator=",">//如果没有用@Param修饰,collection=“list”
-
#{id}
-
</foreach>
-
</delete>
-
-
批量操作
-
delete
-
delete from student where id in ()
-
delete from student where id=1 or id=2
-
-
select
-
select * from student where id in ()
-
select * from student where id=1 or id=2
-
-
update:
-
批量修改
-
-
insert
-
insert into student(name,age) values ("www",24),("yh",25),("xiaowang",18)
-
public int insertMoreStudents(@Param("stuList")List<Student> stuList);
-
<insert id="insertMoreStudents" parameterType="com.njupt.entities.Student">
-
insert into student(name,age) values
-
<foreach collection="stuList" item="stu" separator=",">
-
(#{stu.name}, #{stu.age})
-
</foreach>
-
</insert>
-
-
-
-
sql标签用于抽取可重用sql语句片段,支持判断语句
-
<sql id="insertCols">name,age</sql>
-
<insert id="insertMoreStudents" parameterType="com.njupt.entities.Student">
-
insert into student(<include refid="insertCols"></include>) values
-
<foreach collection="stuList" item="stu" separator=",">
-
(#{stu.name}, #{stu.age})
-
</foreach>
-
</insert>
-
-
内置参数
-
_parameter:代表整个参数
-
单个参数:_parameter就是这个参数
-
多个参数:参数会被封装为一个map,_parameter就是这个map
-
-
_databaseId:如果配置了databaseIdProvider标签
-
_databaseId:就代表当前数据库的别名
-
-
6.缓存
-
一级缓存(本地缓存):其实就是sqlSession对象的一个Map. sqlSession级别的缓存,一级缓存是一直开启的,无法关闭
-
与数据库同一次会话期间查询到的数据会放到本地缓存中,以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库。
-
一级缓存失效情况(即执行同样的sql会访问数据库)
-
sqlSession不同
-
sqlSession相同,查询条件不同
-
sqlSession相同,查询条件相同,但是两次查询期间,执行了增删改操作
-
sqlSession相同,手动清除了缓存,session.clearCache()
-
-
-
二级缓存(全局缓存):基于namespace的缓存,即一个Mapper.XML文件,对应一个二级缓存
-
工作机制
-
一个会话,查询一条数据,这个数据会被放在当前sqlSession的一级缓存中
-
如果会话关闭或者提交,当前缓存会被保存到二级缓存中,新的会话查询信息,可以参照二级缓存。
-
不同的namespace查出的数据会放在自己对应的缓存(map)中
-
-
使用
-
全局配置文件中开启全局二级缓存配置
-
<setting name="cacheEnabled" value="true"/>开启二级缓存
-
-
对要开启二级缓存的Mapper.XML中,启动二级缓存
-
eviction:缓存回收策略
-
LRU-最近最少使用,移除最长时间不被使用的对象,默认
-
FIFO-先进先出,按对象进入缓存的顺序移除他们
-
SOFT-软引用,移除基于垃圾回收器状态和引用规则的对象
-
WEAK-弱引用,更积极地移除基于垃圾回收器状态和引用规则的对象
-
-
flushInterval:缓存刷新间隔,缓存多长时间清空一次,默认不清空,单位毫秒
-
readOnly:缓存是否只读,默认false
-
true:只读,mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改原数据,因此,maybatis直接返回数据的引用,效率较高。
-
false:非只读。获取的数据可能会被用户修改,那么mybatis就不会将原数据的引用交给用户,而是利用序列化技术,返回给用户一个副本。安全,速度慢
-
-
size:默认缓存内存大小
-
type:自定义缓存的全类名,实现Cache接口
-
<mapper namespace="com.njupt.mapper.StudentMapper"> <!-- 接口全类名 -->
-
<cache eviction="FIFO" flushInterval="1000" readOnly="false" ></cache>
-
-
POJO对象实现序列化接口
-
由于二级缓存使用序列化技术,因此POJO对象需要实现Serializable接口
-
-
-
-
和缓存有关的设置
-
<setting name="cacheEnabled" value="false"/>关闭二级缓存,一级缓存一直开着
-
<select id="" useCache="true"></select>,UseCahe标签默认为true,设置是否使用二级缓存,
-
<delete id="deleteMoreStu" flushCache="true"> flushCache默认为true,即每次增删改都会清空一二级缓存
-
sqlSession.clearCache()只会清空一级缓存
-
<setting name="localCacheScope" value="false"/>一级缓存的作用域
-
SESSION:缓存作用域为Session
-
STATEMENT:缓存作用域为Statement,相当于禁用了缓存
-
-
-
缓存的使用顺序,二级缓存-->一级缓存-->数据库
-
缓存的实现原理
-
我们知道缓存就是一个Map,那么其接口为
-
-
Cache接口的实现类
-
-