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接口的实现类
-
-