《Java EE框架整合开发入门到实战》
如有不当,请批评指正!
目录
.......
1,MyBatis
1.1,MyBatis简介
MyBatis 是一个基于Java的持久层框架。MyBatis提供的持久层框架包括SQL Maps和Data Access Objects(DAO),它消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和Java的 POJOs(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
目前,Java的持久层框架产品有许多,常见的有Hibernate和MyBatis。
MyBatis是一个半自动映射的框架,因为MyBatis需要手动匹配POJO、SQL和映射关系;
Hibernate是一个全表映射的框架,只需提供POJO和映射关系即可。
MyBatis是一个小巧、方便、高效、简单、直接、半自动化的持久层框架;
Hibernate是一个强大、方便、高效、复杂、间接、全自动化的持久层框架。
两个持久层框架各有优缺点,开发者应根据实际应用选择它们。
1.2,MyBatis工作原理
(1)读取MyBatis配置文件:mybatis-config.xml为MyBatis的全局配置文件,配置了MyBatis的运行环境等信息,例如数据库连接信息。
(2)加载映射文件。映射文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,需要在MyBatis配置文件mybatis-config.xml中加载。mybatis-config.xml文件可以加载多个映射文件,每个文件对应数据库中的一张表。
(3)构造会话工厂:通过MyBatis的环境等配置信息构建会话工厂SqlSessionFactory。
(4)创建会话对象:由会话工厂创建SqlSession对象,该对象中包含了执行SQL语句的所有方法。
(5)Executor执行器:MyBatis底层定义了一个Executor接口来操作数据库,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。
(6)MappedStatement对象:在Executor接口的执行方法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。
(7)输入参数映射:输入参数类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输入参数映射过程类似于JDBC对preparedStatement对象设置参数的过程。
(8)输出结果映射:输出结果类型可以是Map、 List等集合类型,也可以是基本数据类型和POJO类型。输出结果映射过程类似于JDBC对结果集的解析过程。
1.3, MyBatis创建流程
1.创建Web应用,并添加相关JAR包
2.创建日志文件 log4j.properties
3.创建持久化类
注意在类中声明的属性与数据表的字段一致
4.创建映射文件 XXXMapper.xml
上述映射文件中,<mapper>元素是配置文件的根元素,它包含了一个namespace属性,该属性值通常设置为“包名+SQL映射文件名”,指定了唯一的命名空间。子元素<select>、<insert>、<update>以及<delete>中的信息是用于执行查询、添加、修改以及删除操作的配置。在定义的SQL语句中,“#{}”表示一个占位符,相当于“?”,而“#{uid}”表示该占位符待接收参数的名称为uid。
5.创建MyBatis的配置文件
创建核心配置文件mybatis-config.xml,在该文件中配置了数据库环境和映射文件的位置。
6.创建测试类
在测试类中首先使用输入流读取配置文件,然后根据配置信息构建SqlSessionFactory对象。接下来通过SqlSessionFactory对象创建SqlSession对象,并使用SqlSession对象的方法执行数据库操作。
1.4,MyBatis与Spring的整合
1.导入相关JAR包
(1).MyBatis框架所需的JAR包
MyBatis框架所需的JAR包,包括它的核心包和依赖包,包的详情见6.2节。
(2).Spring框架所需的JAR包
Spring框架所需的JAR包,包括它的核心模块JAR、AOP开发使用的JAR、JDBC和事务的JAR包(其中依赖包不需要再导入,因为MyBatis已提供),具体如下:
aopalliance-1.0.jar
aspectjweaver-1.8.13.jar
spring-aop-5.0.2.RELEASE.jar
spring-aspects-5.0.2.RELEASE.jar
spring-beans-5.0.2.RELEASE.jar
spring-context-5.0.2.RELEASE.jar
spring-core-5.0.2.RELEASE.jar
spring-expression-5.0.2.RELEASE.jar
spring-jdbc-5.0.2.RELEASE.jar
spring-tx-5.0.2.RELEASE.jar
3.MyBatis与Spring整合的中间JAR包
编写本书时,该中间JAR包的最新版本为mybatis-spring-1.3.1.jar。此版本可从地址“http://mvnrepository.com/artifact/org.mybatis/mybatis-spring/1.3.1”下载。
4.数据库驱动JAR包
本书所使用的MySQL数据库驱动包为mysql-connector-java-5.1.45-bin.jar。
5.数据源所需的JAR包
整合时使用的是DBCP数据源,需要准备DBCP和连接池的JAR包。编写本书时,最新版本的DBCP的JAR包为commons-dbcp2-2.2.0.jar,可从地址“http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi”下载;最新版本的连接池的JAR包为commons-pool2-2.5.0.jar,可从地址“http://commons.apache.org/proper/commons-pool/download_pool.cgi”下载。
2.在Spring中配置MyBatis工厂
通过与Spring的整合,MyBatis的SessionFactory交由Spring来构建。构建时需要在Spring的配置文件中添加如下代码:
<!-- 配置数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/springtest?characterEncoding=utf8" />
<property name="username" value="root" />
<property name="password" value="root" />
…….
</bean>
<!-- 配置MyBatis工厂,同时指定数据源,并与MyBatis完美整合 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- configLocation的属性值为MyBatis的核心配置文件 -->
<property name="configLocation" value="classpath:com/mybatis/mybatis-config.xml"/>
</bean>
3.使用Spring管理MyBatis的数据操作接口
使用Spring管理MyBatis的数据操作接口的方式有多种。其中,最常用最简洁的一种是基于MapperScannerConfigurer的整合。该方式需要在Spring的配置文件中加入以下内容:
<!--Mapper代理开发,使用Spring自动扫描MyBatis的接口并装配
(Spring将指定包中所有被@Mapper注解标注的接口自动装配为MyBatis的映射接口) -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- mybatis-spring组件的扫描器。com.dao只需要接口(接口方法与SQL映射文件中相同)-->
<property name="basePackage" value="com.dao"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
2,映射器
2.1,MyBatis配置文件概述
MyBatis的核心配置文件中的元素配置顺序不能颠倒,一旦颠倒在MyBatis启动阶段将发生异常。
2.2,映射器概述
映射器是MyBatis最复杂且最重要的组件,由一个接口加上XML文件(SQL映射文件)组成。MyBatis的映射器也可以使用注解完成,但在实际应用中使用不广泛,原因主要来自以下几个方面:
其一,面对复杂的SQL会显得无力;
其二,注解的可读性较差;
其三,注解丢失了XML上下文相互引用的功能。因此,推荐使用XML文件开发映射器。
2.3,<select>元素
属性名称 | 描 述 |
id | 它和Mapper的命名空间组合起来使用,是唯一标识符,供MyBatis调用 |
parameterType | 表示传入SQL语句的参数类型的全限定名或别名。是个可选属性,MyBatis能推断出 具体传入语句的参数。 |
resultType | SQL语句执行后返回的类型(全限定名或者别名)。如果是集合类型,返回的是集合 元素的类型。返回时可以使用resultType或resultMap之一 |
resultMap | 它是映射集的引用,与< resultMap>元素一起使用。返回时可以使用resultType或 resultMap之一 |
flushCache | 它的作用是在调用SQL语句后,是否要求MyBatis清空之前查询本地缓存和二级缓存。默认值为false。如果设置为true,则任何时候只要SQL语句被调用,都将清空本地缓存和二级缓存 |
useCache | 启动二级缓存的开关。默认值为true,表示将查询结果存入二级缓存中 |
timeout | 用于设置超时参数,单位是秒。超时将抛出异常。 |
fetchSize | 获取记录的总条数设定 |
statementType | 告诉MyBatis使用哪个JDBC的Statement工作,取值为STATEMENT(Statemen t)、PREPARED(PreparedStatement)、CALLABLE(CallableStatement) |
resultSetType | 这是针对JDBC的ResultSet接口而言,其值可设置为FORWARD_ONLY(只允 许向前访问)、SCROLL_SENSITIVE(双向滚动,但不及时更新)、 SCROLL_INSENSITIVE(双向滚动,及时更新) |
<!-- 根据uid查询一个用户信息 -->
<select id="selectUserById" parameterType="Integer"
resultType="com.po.MyUser">
select * from user where uid = #{uid}
</select>
上述示例代码中,id的值是唯一标识符,它接收一个Integer类型的参数,返回一个MyUser类型的对象,结果集自动映射到MyUser属性。
2.3.1,使用Map接口传递多个参数
假设数据操作接口中有个实现查询陈姓男性用户信息功能的方法:
public List<MyUser> selectAllUser(Map<String, Object> param);
此时,传递给映射器的是一个Map对象,使用它在SQL中设置对应的参数,对应SQL文件代码如下:
<!-- 查询陈姓男性用户信息 -->
<select id="selectAllUser" resultType="com.po.MyUser" parameterType="map">
select * from user
where uname like concat('%',#{u_name},'%')
and usex = #{u_sex}
</select>
上述SQL文件中参数名u_name和u_sex是Map的key。
2.3.2,使用Java Bean传递多个参数
首先,创建一个名为com.pojo的包,在包中创建一个POJO类SeletUserParam
其次,将Dao接口中的selectAllUser方法修改为:
public List<MyUser> selectAllUser(SeletUserParam param);
再次,将SQL映射文件UserMapper.xml中的“查询陈姓男性用户信息”代码修改为:
<select id="selectAllUser" resultType="com.po.MyUser" parameterType="com.pojo.SeletUserParam">
select * from user
where uname like concat('%',#{u_name},'%')
and usex = #{u_sex}
</select>
最后,将UserController的“查询多个用户”代码片段修改。
在实际应用中是选择Map还是选择Java Bean传递多个参数应根据实际情况而定,如果参数较少,建议选择Map;如果参数较多,建议选择Java Bean。
2.4,<insert>元素
属性与<select>元素的属性大部分相同,几个特有属性。具体如下:
keyProperty:该属性的作用是将插入或更新操作时的返回值赋值给PO类的某个属性,通常会设置为主键对应的属性。如果是联合主键,可以在多个值之间用逗号隔开。
keyColumn:该属性用于设置第几列是主键,当主键列不是表中的第一列时需要设置。如果是联合主键时,可以在多个值之间用逗号隔开。
useGeneratedKeys:该属性将使MyBatis使用JDBC的getGeneratedKeys()方法获取由数据库内部生产的主键,如MySQL、SQL Server等自动递增的字段,其默认值为false。
2.4.1,主键(自动递增)回填
MySQL、SQL Server等数据库的表格可以采用自动递增的字段作为主键。有时可能需要使用这个刚刚产生的主键,用以关联其他业务。
<!-- 添加一个用户,成功后将主键值回填给uid(po类的属性)-->
<insert id="addUser" parameterType="com.po.MyUser"
keyProperty="uid" useGeneratedKeys="true">
insert into user (uname,usex) values(#{uname},#{usex})
</insert>
2.4.2,自定义主键
如果实际工程中使用的数据库不支持主键自动递增(如Oracle),或者取消了主键自动递增的规则时,可以使用MyBatis的<selectKey>元素来自定义生成主键。
<insert id="insertUser" parameterType="com.po.MyUser">
<!-- 先使用selectKey元素定义主键,然后再定义SQL语句 -->
<selectKey keyProperty="uid" resultType="Integer" order="BEFORE">
select if(max(uid) is null, 1 , max(uid)+1) as newUid from user
</selectKey>
insert into user (uid,uname,usex) values(#{uid},#{uname},#{usex})
</insert>
在执行上述示例代码时,<selectKey>元素首先被执行,该元素通过自定义的语句设置数据表的主键,然后执行插入语句。<selectKey>元素的keyProperty属性指定了新生主键值返回给PO类的哪个属性。order属性可以设置为BEFORE或AFTER,BEFORE表示先执行<selectKey>元素然后执行插入语句;AFTER表示先执行插入语句再执行<selectKey>元素。
2.5,<update>与<delete>元素
<!-- 修改一个用户 -->
<update id="updateUser" parameterType="com.po.MyUser">
update user set uname = #{uname},usex = #{usex} where uid = #{uid}
</update>
<!-- 删除一个用户 -->
<delete id="deleteUser" parameterType="Integer">
delete from user where uid = #{uid}
</delete>
2.6,<sql>元素
<sql>元素的作用在于可以定义SQL语句的一部分(代码片段),方便后面的SQL语句引用它,比如反复使用的列名。
<sql id="comColumns">uid,uname,usex</sql>
<select id="selectUser" resultType="com.po.MyUser">
select <include refid="comColumns"/> from user
</select>
在上述代码中使用<include>元素的refid属性引用了自定义的代码片段。
2.7,<resultMap>元素
<resultMap>元素表示结果映射集,是MyBatis中最重要也是最强大的元素。主要用来定义映射规则、级联的更新以及定义类型转化器等。
2.7.1,<resultMap>元素结构
<resultMap type="" id="">
<constructor><!-- 类在实例化时,用来注入结果到构造方法 -->
<idArg/><!-- ID参数,结果为ID -->
<arg/><!-- 注入到构造方法的一个普通结果 -->
</constructor>
<id/><!-- 用于表示哪个列是主键 -->
<result/><!-- 注入到字段或JavaBean属性的普通结果 -->
<association property=""/><!-- 用于一对一关联 -->
<collection property=""/><!-- 用于一对多、多对多关联 -->
<discriminator javaType=""><!-- 使用结果值来决定使用哪个结果映射 -->
<case value=""/> <!-- 基于某些值的结果映射 -->
</discriminator>
</resultMap>
<resultMap>元素的type属性表示需要的POJO,id属性是resultMap的唯一标识。
子元素<constructor>用于配置构造方法(当POJO未定义无参数的构造方法时使用)。
子元素<id>用于表示哪个列是主键。
子元素<result>用于表示POJO和数据表普通列的映射关系。子元素<association> 、<collection> 和<discriminator>是用在级联的情况下。
2.7.2,使用Map存储结果集
任何select语句可以使用Map存储结果,示例代码如下:
<!-- 查询所有用户信息存到Map中 -->
<select id="selectAllUserMap" resultType="map">
select * from user
</select>
测试上述SQL配置文件的过程如下:
首先在Dao接口中添加以下接口方法
然后在Controller类中调用接口方法
上述Map的key是select语句查询的字段名(必须完全一样),而Map的value是查询返回结果中字段对应的值,一条记录映射到一个Map对象中。Map用起来很方便,但可读性稍差,有的开发者不太喜欢使用Map,更多时候喜欢使用POJO的方式。
2.7.3,使用POJO存储结果集
POJO的方式存储结果集,一方面可以使用自动映射,例如使用resultType属性,但有时候需要更为复杂的映射或级联,这时候就需要使用<select>元素的resultMap属性配置映射集合。
<!-- 使用自定义结果集类型 -->
<resultMap type="com.pojo.MapUser" id="myResult">
<!-- property是com.pojo.MapUser类中的属性-->
<!-- column是查询结果的列名,可以来自不同的表 -->
<id property="m_uid" column="uid"/>
<result property="m_uname" column="uname"/>
<result property="m_usex" column="usex"/>
</resultMap>
<!-- 使用自定义结果集类型查询所有用户 -->
<select id="selectResultMap" resultMap="myResult">
select * from user
</select>
2.8,级联关系
级联的优点是获取关联数据十分方便,但是级联过多会增加数据库系统的复杂度,同时降低系统的性能。在实际开发中要根据实际情况判断是否需要使用级联。更新和删除的级联关系很简单,由数据库内在机制即可完成。
2.8.1,一对一级联查询
通过<resultMap>元素的子元素<association>处理这种一对一级联关系。在<association>元素中,通常使用以下属性。
property:指定映射到实体类的对象属性。
column:指定表中对应的字段(即查询返回的列名)。
javaType:指定映射到实体对象属性的类型。
select:指定引入嵌套查询的子SQL语句,该属性用于关联映射中的嵌套查询。
2.8.2,一对多级联查询
2.8.3,多对多级联查询
其实,MyBatis没有实现多对多级联,这是因为多对多级联可以通过两个一对多级联进行替换。