首页 > 代码库 > Mybatis
Mybatis
一概述
1.什么是Mybatis?
⑴Mybatis是Apache的一个开源项目,原名为ibatis,移植到google code后改名为Mybatis,目前迁移到了Github。
⑵Mybatis是一个支持定制化SQL、存储过程以及高级映射的优秀持久层框架,避免了几乎所有的JDBC代码、手动设置参数以及获取结果集的过程。
2.Mybatis与Hibernate对比
两者的主要区别在于封装程度:
⑴Hibernate封装程度较高,表现为可以自动建立映射关系,即查询结果自动封装为实体类对象,而Mybatis必须手动为结果指明映射的实体类。
⑵Hibernate封装了SQL语句,而Mybatis中的SQL语句需要程序员编写。
⑶Hibernate能够创建表,Mybatis不能创建与修改表,只能修改表中的数据。
⑷正因为Hibernate封装程度较高,获得同样的结果所需代码比Mybatis复杂,执行速度慢,Web应用中速度是至关重要的,所以Hibernate正逐渐被Mybatis取代。
3.Mybatis体系结构,由上至下:
⑴API接口:
Mybatis的顶层,Mybatis提供给开发人员的用于操作数据库的入口,主要是一些对象中的方法。
⑵数据处理层:
顶层的API接口被调用以后,触发数据处理层,该层具体负责SQL查找、SQL解析、SQL执行、结果映射。
⑶基础支撑层:
为整个框架的运行提供了基础性质的支撑,主要有配置加载、连接管理、事务管理、缓存管理。
4.Mybatis基本执行过程:
启动时加载配置文件,为映射文件中的每一个SQL语句创建一个MappedStatement对象,通过API接口调用SQL语句,系统根据id找到对应的MappedStatement对象,解析成SQL语句,连接数据库执行,最后将查询结果映射成属性或者对象。
5.配置文件:
<configuration> <!--加载包含数据库连接四要素的属性文件 --> <properties resource="dbConnection.properties"/> <!--为映射文件中的类起一个别名,这样在映射文件中可以省去书写全限定性类名,直接使用别名 --> <typeAliases> <typeAlias type="com.mybatis.beans.demo01.Student" alias="Student"/> <!--<package name="com.mybatis.beans.demo01" /> --> </typeAliases> <!--配置环境,可以有多个环境,比如测试环境,发布环境,default选择使用的环境。环境指的是数据库管理方面的信息,比如数据源、事务--> <environments default="development"> <environment id="development"> <!--表示采用JDBC默认的事务管理器,项目开发时用DataSourceTransactionManger--> <transactionManager type="JDBC" /> <!--采用Mybatis自带的数据库连接池,项目开始时通常使用C3P0或者DBCP --> <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> <mapper resource="com/mybatis/beans/demo01/Student.xml" /> </mappers> </configuration>
配置文件主要包含:
⑴数据库连接四要素通常放在属性文件中,便于更改。为每一个环境都创建一个属性文件,key相同,当需要加载某个环境时,只需要将总环境的默认值设为该环境的id。从属性文件中读取数据的基本语法格式:${key}。
⑵<typeAliases>用于为类指定一个别名:
①<typeAlias type="com.mybatis.beans.demo01.Student" alias="Student"/>:为全限定性类名指定一个别名,在映射文件中使用该别名替代全限定性类名,使代码简洁。
②<package name="com.mybatis.beans.demo01" />:指定包名,在没有注解的情况下,系统采用非限定性类名作为别名,首字母可以小写。
⑶<environments>:配置文件中可以有多个环境,分别用于不同的用途,default属性指定当前使用的环境。
⑷<environment>:主要包含事务管理器、数据源等。
⑸<mappers>:将映射文件加载到配置文件中。
6.映射文件:
<mapper namespace="student"> <insert id="insertStudent"> insert into tb_student(name,score)values(#{name},#{score}) ----------在数据插入完成后获取插入数据的id------------ <selectKey resultType="int"keyProperty="id"> select@@identity </selectKey> </insert> <delete id="deleteStudent">delete from tb_student whereid=#{?}</delete> ------------------------------模糊查询------------------------------- <select id=""resultType="xxxx">select name from xxx where name like ‘%‘#{}‘%‘</select> <mapper>
⑴映射文件用于设定字段与属性的映射关系。
⑵namespace:命名空间,即名称存在的范围,用于区分不同映射文件中同名的SQL。
⑶id:在同一个映射文件中不可重复,用于在程序中调用该SQL。
⑷parameterType:当参数类型是实体类时,可以直接使用配置文件中的别名。
⑸select:有返回值,返回值可以是属性,也可以是实体类对象,因此必须指定单行数据返回值类型resultType,以便系统按照该类型返回查询结果。
⑹Mybatis为常见的java类型设定了别名,别名大小写不敏感,即大小写作用相同。基本数据类型的别名在前面加下划线,如_int,其他常用类型的别名就是首字符小写后的非限定性类名,如String的别名是string,由于大小写不敏感,也可以写成String。
⑺语法介绍:
①#{属性名}:获取对象属性值;
②#{xxx}:用作占位符,可以是任意合法字符,用于参数不是实体类对象时取得参数的值。
⑻模糊查询语法格式:‘%’#{xxx},为了避免系统将占位符视作普通字符处理,占位符放在单引号或者双引号外面,与单引号或者双引号之间不使用任何连接。
7.默认封装时,采用反射机制,封装时根据字段名调用set方法初始化对象,因此表中的字段名必须与实体类中对应的属性名相同,如果字段名与属性名不同,那么该属性无法被初始化。
属性名与字段名不一致时的解决方案:
①使用别名:通过别名将查询结果的字段名修改为属性名,缺点是每一次查询时都需要创建别名。
②使用resultMap:采用Hibernate的处理方式,手动在字段与属性之间建立映射:
<resultMap type="studentResultMap"id="studentMap"> <id column="id" property="sid" /> <result column="name" property="sname" /> </resultMap>
8.SqlSessionFactory
⑴相当于Hibernate中的SessionFactory对象,主要负责创建SqlSession对象。创建初始化过程耗费大量系统资源,并且线程安全,因此将对象设定为应用级的,即在整个应用范围采用单例模式。
⑵创建:
InputStream input=Resources.getResourceAsStream("文件的类路径");//基于路径创建输入流 SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(input);
输入流工厂对象创建完毕后自动关闭,不需要手动关闭。
9.SqlSession
⑴Mybatis持久化操作的核心,线程不安全,应该设置为多例的,即一个线程一个。
⑵创建:
SqlSession sqlSession=factory.openSession();//使用openSession的无参形式时,自动关闭自动提交
⑶主要方法:
①sqlSession.close():关闭sqlSession对象,如果未回滚,回滚。
⑷dirty:一个框架底层的boolean值,主要用于判断当前缓存中内容与数据库中内容是否相同,相同为false,不同为true,数据提交或者回滚时需要判断该值。
10.使用log4j技术输出日志信息。
二 Mapper动态代理
1.如果Dao接口的方法名与映射文件中SQL语句的id相同,那么根据方法名就可以找到对应的SQL语句,从而省去将方法与SQL语句绑定的过程。需要做两点:方法名与id相同,命名空间为Dao接口的全限定性类名。
2.怎么在java代码中获取操作对象?
由于采用Mapper动态代理,省去了Dao实现层,只剩下Dao接口,而Dao接口无法实例化,那么就根据该接口创建一个代理对象,作为操作对象:
IDao dao=sqlSession.getMapper(IDao.class);
3.基本原理
利用JDK动态代理创建了一个代理对象,该代理对象在Dao方法与映射文件SQL语句间建立了映射关系,所以称作
MapperProxy。
三多条件查询
1.什么是多条件查询?
根据多个条件进行查询,叫做多条件查询,就是查询时传入多个参数,称作多参数查询更准确。
2.多参数查询问题产生的原因?
同时向SQL语句传入多个参数,就出现了一个对应问题,传入的参数与SQL语句中的哪个占位符对应,因此产生了多参数查询问题。Mybatis中进行持久化操作的方法只允许存在一个传入SQL语句的参数。
3.解决方法
⑴手动封装:为每一个参数设定一个key值,将多个key/value保存到Map对象中,将Map对象作为参数传入SQL语句,在SQL语句中通过 #{key}获取参数value的值。
⑵自动封装:方法定义时设定key。
Student selectStudentByIndex(@Param("name")String name,@Param("score") double score);
⑶高版本多参数查询不支持索引。
四 #在映射文件中的用法
1.#{attr}:当参数是实体类对象时,获取属性值;
2.#{?}:当参数是基本数据类型或者字符串时,用作占位符,获取参数的值,{}内部可以是任意合法字符,一般用属性名。
3.#{key}:当参数是Map集合时,通过key值获取value的值。
五动态SQL
1.什么是动态SQL?
根据传入的参数选择性执行SQL语句,这一过程叫做动态SQL.
2.动态SQL中用到的字符:
<if>\<where>\<choose when otherwise>\<foreach>。
3.<where if>:
从多个过滤条件中选择若干个。
⑴基本格式:
<where> <if test="scoreKey>0">score>#{scoreKey}</if> <if test="nameKey!=null &nameKey!=‘‘">and name like"%"#{nameKey}"%"</if> </where>
⑵test用来指明判断条件,其中使用key值;为了完成组合,除第一个<if>标签外,其他标签的内容都必须以连接符开头,如and,or。
⑶映射文件是一个XML文档,而在XML文档中不允许使用> >= < <= &,必须使用对应的实体替换。
4.<where choose when otherwise>:
从多个条件中选择第一个为真的执行。
基本格式:
<where> <choose> <when test="nameKey!=null and nameKey!=‘‘"> name like"%"#{nameKey}"%"</when> <when test="scoreKey > 0">score>#{scoreKey}</when> <otherwise>1=2</otherwise> </choose> </where>
5.<where foreach>:遍历列表。
⑴基本格式:
<select id="selectStudentsByArray"resultType="student"> select id,name,score from tb_student <where> <if test="array.length>0"> id in <foreach collection="array"item="myid" open="(" close=")"separator=","> #{myid} </foreach> </if> </where> </select>
⑴当参数类型是数组时,collection值为array;参数类型是List时,collection值为list。
⑵item表示遍历的子对象,集合或者数组中一个元素。
⑶open/close/separator:用于拼凑JDBC中的(a,b,c)的列表形式。
六实体关联查询
1.什么是实体关联查询?
两个实体间存在关联关系,即引用关系,通过一个实体查询关联的实体,这就是实体关联查询。
2.关联关系查询时必须手动地为字段建立与属性的映射关系。
3.一对多关联查询:
映射文件的编写:
<resultMap id="countryMapAlone"type="Country"> <id property="cid" column="cid" /> <result property="cname" column="cname" /> <collection property="ministers" ofType="Minister"select="ministerSelect"column="cid" /> </resultMap> <select id="selectCountryByIdAlone" resultMap="countryMapAlone"> selectc id,cname from tb_country where cid=#{cid} </select> <select id="ministerSelect" resultType="Minister"> select mid,mname,countryId from tb_minister where countryId=#{cid} </select>
在<collection>标签中:
⑴ofType:指明集合中的每一个元素的类型。
⑵select属性用来指明从加载方的查询语句。
⑶column属性指明将主加载方查询结果的哪个字段传入从加载方的查询语句中。
4.多对一关联查询:
映射文件编写:
<select id="selectMinisterById"resultMap="ministerMap"> select mid,mname,countryId from tb_minister where mid=#{id} </select> <resultMap id="ministerMap"type="com.mybatis.entityRelation.m2o.Minister"> <id property="mid" column="mid" /> <result property="mname" column="mname" /> <result property="countryId" column="countryId" /> <association property="country"javaType="com.mybatis.entityRelation.m2o.Country" select="selectCountry"column="countryId" /> </resultMap> <select id="selectCountry" resultType="com.mybatis.entityRelation.m2o.Country"> selectc id,cname from tb_country where cid=#{countryId} </select>
5.自关联:
⑴什么是自关联?
两个实体类除了一个关联属性外其他属性完全相同,将两个实体当做一个实体处理,这样关联关系就转化为了自关联。
⑵把自关联看做一对多双向关联。
⑶查找自身及其子对象:
<select id="selectNewsLabelsById"resultMap=""> select id,name,pid from tb_newslabel where id=#{id} </select> <resultMap id="" type="newsLabel"> <id property="id" column="id" /> <result property="name" column="name" /> <result property="pid" column="pid" /> <collection property="childNewsLabels" ofType="newsLabel"select="selectChild"column="id" /> </resultMap> <select id="selectChild" resultType="newsLabel"> select id,name,pid from tb_newslabel where pid=#{id} </select>
6.多对多关联查询:
⑴将多对多关联看做两个一对多关联,一方是原有两方中的任一方,多方是中间表,中间表有两个外键,分别引用其他两张表的主键。
⑵多对多关联查询多采用表连接查询,同时查询三张表,因为这种操作方式简单。
⑶多表联合查询分别查询:
<select id="selectStudentByIdAlone" resultMap="aloneMap"> select sid,sname from tb_studentc where sid=#{sid} </select> <resultMap id="aloneMap"type="com.mybatis.entityRelation.m2m.Student"> <id property="sid" column="sid" /> <result property="sname" column="sname" /> <collection property="courses" ofType="com.mybatis.entityRelation.m2m.Course" select="selectCourse"column="sid" /> </resultMap> <select id="selectCourse"resultType="com.mybatis.entityRelation.m2m.Course"> select cid,cname from tb_course where cid in (select course_id from tb_mid where student_id=#{sid})//采用临时表 </select>
七延时加载
1.什么是延时加载?
加载主加载方时,不立即加载从加载方,而是根据设定延迟加载从加载方。
2.延时加载针对的关联查询时的从加载方,设定从加载方的加载时机。
3.Mybatis中加载从加载方的几种方式:
⑴直接加载:主加载方加载完毕后,立即加载从加载方,未开启延时加载时采用该加载方式。
⑵侵入式延迟:将从加载方当做主加载方的详情,访问主加载方的详情时,加载从加载方。
⑶深度延迟:只有在访问从加载方对象时才加载从加载方。
4.延时加载的设置:
⑴默认情况下,延迟加载是关闭的,主加载方加载完毕后立即加载从加载方。在配置文件中开启延时加载:
<settings> <setting name="lazyLoadingEnabled" value="true" /> </settings>
⑵开启延迟加载以后,默认采用深度延迟。
⑶如需要采用侵入式延迟:
<settings> <setting name="lazyLoadingEnabled" value="true" /> <setting name="aggressiveLazyLoading" value="true" /> </settings>
八缓存
1.缓存是内存中的临时存储区,用来存放从数据库中加载的数据,目的是为了提高查询速度。
2.缓存内容的来源:SQL查询从数据库中加载的数据。
3.缓存内容的类型:封装的实体类对象与分散的属性值。
4.Mybatis中用两种缓存:一级缓存,二级缓存。
5.无论一级缓存,还是二级缓存,只在同一namespace中可见,对其他namespace的查询不可见。
6.缓存的存储格式:缓存以Map集合的形式存储内容,key是SQL id与SQL语句的组合,value是从数据库中加载的内容。
7.无论一级缓存,还是二级缓存,缓存查找依据都是SQLid与SQL语句,即根据SQL id与SQL语句查找缓存中是否具有相同的内容。
8.一级缓存与二级缓存持有的是同一对象的内存地址,执行增删改操作,将会清空一级缓存,即销毁引用地址指向的对象,同时默认清空二级缓存。清空的是缓存Map集合的value值,保留key值。
9.一级缓存:
⑴一级缓存:SqlSession级的,缓存中的内容只能在同一个SqlSession内部共享,生命周期与SqlSession相同。
⑵一级缓存是默认的缓存,无法关闭,也无法通过设置改变属性,即只能采用默认的方式,不能自定义。
⑶sqlSession关闭意味着Session对象销毁,一级缓存释放持有的引用地址,而不是销毁对象。
10.Mybatis提供了两种二级缓存方案:内置二级缓存与第三方二级缓存Ehcache。
⑴二级缓存默认情况下是开启的。
⑵二级缓存不会主动加载对象,要想将对象加载到二级缓存中,必须在命名空间下开启二级缓存:<cache/>,该命名空间下所有查询操作的结果都会被保存到二级缓存中。
⑶二级缓存的关闭:
①应用范围关闭:<setting name="cacheEnabled" value="http://www.mamicode.com/true" />;
②命名空间范围关闭:注销掉命名空间下的<cache/>.
③SQL范围关闭:<select useCache="false"/>
⑷二级缓存使用原则:
①避免多个namespace操作同一张表:二级缓存是基于namespace的,不同namespace对二级缓存的操作是隔离的,相当于每一方都把数据库中的数据读取到内存中,各自独立操纵,这样当一方的操作同步到数据库以后,另一方还在操作数据库中改变了的数据,造成幻读。
②不要在关联关系表上执行增删改操作:不同的表对应不同的namespace,同一张表可能同时被多个namespace操作,导致幻读。
③查询多于增删改时使用二级缓存:增删改清空缓存,消耗系统资源。
⑸开启内置二级缓存:
①序列化实体类:应用内置二级缓存的实体类必须序列化。
②<cache/>:在指定命名空间使用二级缓存。
⑹使用Ehcache第三方缓存Ecache:
①导入架包;
②<cache type="org.mybatis.caches.ehcache.EhcacheCache" />:在命名空间指定使用Ehcache二级缓存;
③导入Ehcache配置文件ehcache.xml,放在类路径下。
⑺默认情况下,执行增删改操作会清空二级缓存,如果不希望某个增删改操作清空二级缓存,可以在映射文件<insert/delete/update>标签下设置,将flushCache设为false。由于一级缓存与二级缓存引用的是同一对象,如果不希望清空二级缓存,不能在一级缓存持有引用地址的情况下执行增删改操作。
九注解
1.注解是在java类文件中添加注解以替代映射文件的注册方式。
2.为了充分发挥框架的功能,Mybatis推荐使用映射文件,不建议使用注解。
3.常用注解:
⑴@Insert()/@Delete()/Update()。
⑵@Select(value=""):使用注解查询时可以省略结果类型。
⑶@SelectKey(statement = "select@@identity",resultType=Integer.class,keyProperty = "id", before =false):获取刚插入数据的主键值,必须与插入注解联合使用。
⑷在配置文件中注册Dao接口:
①<mapper class=""/>:通过Dao类注册。
②<package name="Dao类所在的包"/>:通过包名注册,可以同时注解该包下的多个Dao接口。
Mybatis