数据访问层框架,ORM,Object Relation Mapping,将对象映射到关系,通过操作对象来达到操作关系的目的。
通过hibernate访问数据层不需要自己编写sql,简单智能,调用方法就可完成。ibatis需要自己编写sql语句,更加灵活,可对sql进行优化。ibatis的最后版本为2.3.4,从3.x开始,转投到Google Code门下,并改名为MyBatis。
===========================================
ibatis的核心类是SqlMapClient,通过其可完成对数据库的访问操作。
spring提供的SqlMapClientFactoryBean对SqlMapClient进行了封装,类内提供对SqlMapClient实例进行初始化的方法,并提供返回SqlMapClient实例的方法getObject。SqlMapClientFactoryBean实现FactoryBean接口,spring规定实现该接口的Bean,由框架实例化时,返回的是getObject返回的实例,这也体现了SqlMapClientFactoryBean是SqlMapClient的实例工厂的意义——配置SqlMapClientFactoryBean,要注入configLocation属性,对应ibatis的配置文件;datasource属性,对应数据源bean。
spring提供的SqlMapClientTemplate对SqlMapClient进行了封装,类内实际是对SqlMapClient的增删改查方法做了一个外包层,类内SqlMapClient实例的初始化需要外部的注入,本身不提供初始化方法。SqlMapClientTemplate继承JdbcAccessor,JdbcAccessor内部封装了DataSource数据源Bean——配置SqlMapClientTemplate,要注入sqlMapClient属性,对应注入SqlMapClientFactoryBean的实例,因为其返回的就是SqlMapClient的实例;datasource属性,对应数据源bean。
spring提供的SqlMapClientDaoSupport对SqlMapClientTemplate进行了封装,是一个抽象类,必须继承才能使用,类内以new的方式生成一个SqlMapClientTemplate的实例。
DataSource可在SqlMapClientFactoryBean内注入,也可在SqlMapClientTemplate内注入,放在SqlMapClientTemplate内更合适。
实际项目中肯能会有多个数据源,如mysql、sqlserver、oracle等。
SqlMapClientFactoryBean旨在生成SqlMapClient的实例,与属性configLocation有关,需要指明映射关系,而SqlMapClient实例的生成与属性datasource无关,仅在连接数据库时会用到。
SqlMapClientTemplate内部使用SqlMapClient的实例进行数据库操作,需要连接数据库,必须注入属性datasource。
针对不同的数据源,需要对每个数据源配置一个SqlMapClientFactoryBean用于注入对应数据源的映射文件,需要对每个数据源配置一个SqlMapClientTemplate用于注入数据源bean来连接不同的数据库。
dao的三种实现方式,根据设计模式能用对象组合不用继承的原则,不建议使用后两种方式。
对象组合方式,dao内部定义SqlMapClientTemplate的实例,SqlMapClientTemplate的实例由spring注册并注入,SqlMapClientTemplate要注入SqlMapClientFactoryBean与DataSource。
继承方式,dao继承SqlMapClientTemplate,dao的配置也就包括了SqlMapClientTemplate的配置,dao内部可以直接使用继承的方法操作数据库,dao配置要注入SqlMapClientFactoryBean与DataSource——类内属性不加注解,配置文件内也不给类注入,配置文件头部<beans default-autowire="byName">
给出default-autowire,spring也能完成注入。
继承方式,dao继承SqlMapClientDaoSupport,同样dao的配置也包括了SqlMapClientTemplate的配置,dao内部可以直接使用继承的SqlMapClientTemplate实例操作数据库,dao配置要注入SqlMapClientFactoryBean与DataSource——类内只给出set方法,不给出属性定义,spring也能完成注入。
===========================================
ibatis中一个pojo对应一个dao,一个dao对应一个映射文件,由此项目中通常会有多个映射文件。由于ibatis最终会将所有的映射文件合并到一起,为防止id冲突,需要命名空间namespace来唯一标识该映射文件。
以下两个配置文件在dao层,属于ibatis的内容。
// Computer.xml
// namespace,通常用pojo名
<sqlMap namespace="Computer">
// alias,类型别名,通常为首字母小写的pojo名
<typeAlias alias="computer" type="xx.Computer" />
// 对应一类查询结果集
// class,指明查询结果要映射的pojo类
<resultMap id="all" class="computer">
// 指明pojo类的属性与关系表中字段的对应关系
// jdbcType与javaType的对应关系用时去网上查一下,不要背
<result property="id" column="id" jdbcType="INT" javaType="java.lang.Integer" />
<result property="name" column="name" jdbcType="VARCHAR" javaType="java.lang.String" />
<result property="price" column="price" jdbcType="VARCHAR" javaType="java.lang.String"/>
</resultMap>
<resultMap id="part" class="computer">
<result property="name" column="name" jdbcType="VARCHAR" javaType="java.lang.String" />
<result property="price" column="price" jdbcType="VARCHAR" javaType="java.lang.String"/>
</resultMap>
// id,指出该sql语句的名字,代码中会用到,用时必须加命名空间
// parameterClass,指出参数类型
<insert id="insert" parameterClass="computer">
// CDATA中的内容被定义为纯文本,以防与xml标签混淆
// 在#之间的为变量
<![CDATA[
insert into computer(name, price) values(#name#, #price#)
]]>
// selectKey,用于在插入时自动生成主键
// keyProperty,定义主键名字
// resultClass,定义返回类型
<selectKey resultClass="java.lang.Long" keyProperty="id">
// 返回上次插入自动生成的id
select @@IDENTITY as id
</selectKey>
</insert>
// 项目中不要用基本类型,要用类包装
// 也有将删除操作放在update标签内的,没多大区别
<delete id="delete" parameterClass="java.lang.Long">
delete from computer where id = #id#
</delete>
<update id="update" parameterClass="computer">
update computer set name = #name#, price = #price# where id = #id#
// 或
// dynamic的prepend值会覆盖条件标签中第一个条件为真的prepend的值
// 前提是条件标签prepend的值被设置且长度 > 0
update computer
<dynamic prepend="set">
<isNotEmpty property="name" prepend=','>
name = #name#
</isNotEmpty>
<isNotEmpty property="price" prepend=','>
price = #price #
</isNotEmpty>
</dynamic>
where id = #id#
</update>
// select查询出的字段必须和resultMap配置的字段对应,多了、少了或不对应都会抛出异常
// ibatis内部会将查询结果依次赋值到resultMap配置的pojo实例内对应的属性上
// resultMap,为前面定义的resultMap的id
<select id="list" resultMap="all">
select id, name, price from computer
</select>
// 或
// resultMap是显示映射,pojo属性与关系字段映射明确,性能更好,推荐使用
// resultClass,指出查询结果要映射的类,为隐式映射,若pojo的属性名与关系的字段名不同,查询结果就不会映射到类
<select id="list" resultClass="computer">
select id, name, price from computer
</select>
// 查询结果仅一个字段
<select id="getName" parameterClass="java.lang.Long" resultClass="java.lang.String">
select name from computer where id = #id#
</select>
// sql标签用来定义sql语句片段
// id,为sql片段标识,后面可根据此id拼接sql语句
<sql id="list_where">
// isNotNull,指当property设置的变量name不为空时,执行标签里内容
// prepend,表示将其内容and追加在isNotNull标签的内容之前
<isNotNull property="name" prepend="and">
name = #name#
</isNotNull>
<isNotNull property="price" prepend="and">
price = #price#
</isNotNull>
// 上述打印内容为 and name = xx and price = xx
// 通过设置dynamic标签的prepend来覆盖第一个条件为真的and
// 打印内容为where name = xx and price = xx
<dynamic prepend="where">
<isNotNull property="name" prepend="and">
name = #name#
</isNotNull>
<isNotNull property="price" prepend="and">
price = #price#
</isNotNull>
</dynamic>
</sql>
// parameterClass,参数为map类型
<select id="list_2" parameterClass="map" resultMap="part">
// where 1 = 1,用于巧妙的连接name之前的and,若用dynamic可以不用此
// include,用于包含sql片段
// start,属性值来自map类型的参数
// limit start, rows 返回查询结果的第start行,偏移从0开始,共返回rows行
// limit start, -1 返回查询结果的第start,及其之后的所有行
// limit end 等价于 limit 0, end 返回查询结果第end行,及其之前的所有行
// #与$的区别,#之间放变量,$用于字符串之间的拼接,$间不要放字符或字符串变量,否则出错
select name, price from computer
where 1 = 1
<include refid="list_where" />
order by id asc
<isNotNull property="start" prepend=" ">
limit $start$, $rows$
</isNotNull>
</select>
</sqlMap>
// sqlmap-config-mysql.xml
// 用于配置数据源信息,以及整合全部的映射文件
// 若有其它异构数据库,应单独建立另一个文件,如sqlmap-config-sqlserver.xml
<sqlMapConfig>
// useStatementNamespaces,设为true表示开启映射文件的命名空间
<settings useStatementNamespaces="true"/>
// 配置数据源,与spring整合后,该部分移植到spring配置文件
....
// 以当前目录为相对路径,加载映射文件
<sqlMap resource="xx/mysql/xx.xml" />
</sqlMapConfig>
===========================================
以下配置文件在web层,属于spring与ibatis的整合内容。
// spring-config-datasource-dbcp.xml
// 所有的数据源在此配置
<beans>
// 将数据源注册为bean
<bean id="mysqlDataSource" class="..BasicDataSource">
// 属性的名字都是在BasicDataSource里定义好的,不要自定义
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/xxdb" />
<property name="username" value="root" />
<property name="password" value="123" />
</bean>
// 其它的数据源,如sqlserver、oracle、分库的
</beans>
// spring-dao.xml
// 注册项目所有的dao
<beans>
// 注册SqlMapClientFactoryBean
<bean id="sqlMapClient" class="..SqlMapClientFactoryBean">
// SqlMapClientFactoryBean源码内定义了属性dataSource和configLocation
// dataSource放在SqlMapClientTemplate内注入
// configLocation在此注入,看为与ibatis整合的入口
<property name="configLocation" value="classpath:sqlmap-config-mysql.xml" />
</bean>
// 针对不同的数据源,要再次配置新的SqlMapClientFactoryBean
....
// 配置SqlMapClientTemplate
<bean id="mysqlSqlMapClientTemplate" class="..SqlMapClientTemplate">
<property name="dataSource" ref="mysqlDataSource">
// SqlMapClientTemplate源码内定义了ibatis的SqlMapClient类型的属性sqlMapClient
<property name="sqlMapClient" ref="sqlMapClient">
</property>
// 针对不同的数据源,要再次配置新的SqlMapClientTemplate
....
// 放到项目用注解完成
<bean id="xxDaoImpl" class="xx.xxDaoImpl">
<property name="myClientTemplate" ref="mysqlSqlMapClientTemplate" />
</bean>
</beans>
// spring-config.xml
// 将上述xml文件整合到一起
@Repository("computerDao ")
public class ComputerDaoImpl implements ComputerDao {
@Resource(name = "mysqlSqlMapClientTemplate")
private SqlMapClientTemplate mysqlTool;
..
public Integer insertComputer(Computer computer) {
// 加命名空间
return (Integer)mysqlTool.insert("Computer.insert", computer);
}
}