对于持久层框架,我们比较熟悉的是Hibernate,这是一款功能非常强大的持久框。除此之外,还有一款比较流行的框架 - Mybatis
什么是Mybatis?
mybatis是数据映射器(数据映射器层:用于在对象和数据库之间搬运数据,同时保证对象、数据库和数据映射器层本身相对独立。Martin Fowler 《企业应用架构模式》) mybatis不是直接把类映射为数据库表,而是把sql语句的参数与结果(即输入与输出)映射为类。为如何在类和数据库间建立映射带来了更大的灵活性。同时也更好的隔离了数据库设计和应用程序中使用的对象模型。
从以上描述上可以很容易的看出Mybatis是将结果集映射成类,这种半自动化持久框架的最大特点就是具有很强的灵活性。何为半自动化,就是相对于Hibernate的全自动而言的,因为Mybatis需要为每一个持久层的接口定义一个sql语句,接口的执行,就是执行这些sql,每一个持久层方法都需要手动设置sql语句,所以更加灵活
对于Mybatis更多的介绍请参阅黎明你好的技术博客博主的博客http://limingnihao.iteye.com/blog/781671,比较基础和详细。
可以看出mybatis持久框架主要涉及到了三个类,分别是Model模型,是要将结果集映射成的数据模型,Mapper 接口文件,对应持久层的接口;还有一个xml配置文件,用sql语句实现Mapper的接口。以下给出三种文件的例子:
Domain.java
public class Domain {
private String id;
private String name;
private String domainCode; //
// 省略getter setter
}
DomainMapper.java:
public interface DomainMapper {
public Domain findByID(String domainID);
public List<Domain> findAll();
public void add(Domain domain);
public void merge(Domain domain);
public void delete(String domainID);
}
DomainMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.test.pojo.domain.DomainMapper">
<resultMap type="Domain" id="DomainResultMap">
<id property="id" column="ID"/ jdbctype=VARCHAR>
<result property="name" column="Name"/ jdbctype=VARCHAR>
<result property="domainCode" column="DomainCode"/ jdbctype=VARCHAR>
</resultMap>
<!-- 查询域,根据id -->
<select id="findByID" parameterType="String" resultMap="DomainResultMap">
<![CDATA[
SELECT * from Domain D
WHERE D.ID = #{doaminID}
]]>
</select>
<select id="findAll" resultMap="DomainResultMap">
<![CDATA[
SELECT * from Domain
]]>
</select>
<insert id="add" parameterType="Domain">
<![CDATA[
INSERT INTO DOMAIN (ID,
NAME, DomainCode)
VALUES ( #{id},
#{name}, #{domainCode})
]]>
</insert>
<update id="merge" parameterType="Domain">
<![CDATA[
UPDATE DOMAIN
SET NAME = #{name}
WHERE ID = #{id}
]]>
</update>
<delete id="delete" parameterType="String">
<![CDATA[
DELETE DOMAIN
WHERE ID = #{domainID}
]]>
</delete>
</mapper>
[CDATA]是保证里面的内容当成语句执行,可以不加,不加的时候注意一些关键字,比如User ,要写成‘User’格式
Mapper.java与Mapper.xml的方法一一对应,而且方法名必须一致
namespace指向Mapper.java文件,resultMap 是结果集映射,type是映射到的实体类,这里之所以没有写全路径,是因为Mybatis配置文件里可以对实体类定义别名。
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias alias="Domain" type="com.shenj.teworks.pojo.domain.Domain"/>
</typeAliases>
<mappers>
<mapper resource="com/shenj/teworks/pojo/domain/DomainMapper.xml" />
</mappers>
</configuration>
所有的Mapper.xml文件都要在上面这个文件做一个声明;
在spring中定义datasource:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver">
</property>
<property name="url" value="jdbc:jtds:sqlserver://localhost:1433/Test">
</property>
<property name="username" value="sa"></property>
<property name="password" value="sa"></property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml" />
<property name="dataSource" ref="dataSource" />
</bean>
然后Mapper就可以在Spring里声明了,持久层接口就实现了。
应用Mybatis,最经常出现问题的就是xml配置文件:
ResultType取值
可以是一个对象,或者基本类型如,java.lang.Strin,java.lang.Integer
JDBCType是字段对应的数据库类型,取值如下:
BIT,NVACHAR,BIT,TIMESTAMP…
注意
- 对于可能为空的字段,进行赋值的时候必须指明jdbcType
- 对于有些数据库的nvarchar类型,jdbcType指定为NVARCHAR会报错,请改为VARCHAR即可
Mapper参数设置:
如果只有一个参数,可以这样赋值
ID = #{id , jdbcType=VARCHAR}
或者使用编号,代表第几个参数
ID = #{0}
如果存在不同类型的参数,在xml方法里就不能指明parameterType,
设置参数的时候有两种选择
1 接口
pulic User find(Strig id , String year);
xml中使用序号获取参数 如
where ID = #{0} and Year = #{1}
2 map,在server中将所有参数,封装成map
接口如下:
public User find(Map map);
Map map = new Map();
map.set("id", id);
map.set("year" ,year);
在xml中使用的时候直接用key
where ID = #{id} and
Year = #{year}
此处名称id,year就是map设置的key,必须保持一致
association:
外键映射成对象,比如Schedule 表中有外键Creator 连接到表User表, Schedule对象中有User类型的creator属性,此时就要用association.
有两种方法:
1.用select ,如下
<association property="creator" column="Creator" javaType="User"
select="com.test.pojo.user.UserMapper.findByI D"/>
此时在获取Schedule对象的时候,根据外键Creator调用userMapper的select方法(UserMapper里事先已经实现了该方法),相当于再去User表中查询一次。
2.用resultMap,如下
<association property="creator" column="Creator" javaType="User"
resultMap="com.test.pojo.user.UserMapper.UserResultMap"/>
此时相当于把UserResultMap集成到ScheduleResulMap中,然后在获取的时候 将两张表连接在一起(此处保证UserMapper下的UserResultMap已经定义了)
select * from Schedule left join User on Creator = UserID
在此需要保证Schedule和User的字段都不一样,否则ResultMap也不知道究竟映射哪一个了,第一种方法没有这种要求
从速度上来说,第一种方法做了两次查询,后一种方法只做了一次查询,因此速度更快,不过一定要保证两张表的字段不能重复,如果不能保证,还是保险一点使用第一种方法吧,如果对于性能要求比较高,再进行优化。
Collection
当处理一对多关系时,映射的是一个关系表中的数据集合,跟association一样,也有两种方法
1.用select
<collection property="appointmentParticipants" column="ID" ofType="User"
select="com.test.pojo.schedule.AppointmentParticipantMapper.findParticipantByAppointment">
</collection>
同理,进行两次查询, 只不过这个查询的结果是一个集合,通过ofType制定元素的类型
2.用ResultMap
<collection property="appointmentParticipants" column="ID" ofType="User"
ResultMap="com.test.pojo.schedule.AppointmentParticipantMapper.UserResultMap">
</collection>
表连接的方式进行一次查询。