当前位置: Coin163 >>

Mybatis教程及使用配置

2013-11-22 | 所属分类:Mybatis 教程 入门 配置
一.Mybatis简介
MyBatis由Clinton Begin 在2002 年创建,其后,捐献给了Apache基金会,成立了iBatis 项目。2010 年5 月,将代码库迁至Google Code,并更名为MyBatis。
MyBatis 是一个可以自定义SQL、存储过程和高级映射的持久层框架。MyBatis 只使用简单的XML 和注解来配置和映射基本数据类型、Map 接口和POJO 到数据库记录。相对Hibernate和Apache OJB等“一站式”ORM解决方案而言,Mybatis 是一种“半自动化”的ORM实现。
二.从JDBC到Mybatis
1.  JDBC查询数据库数据,一般需要以下七个步骤:
(1)  加载JDBC驱动
(2)  建立并获取数据库连接
(3)  创建 JDBC Statements 对象
(4)  设置SQL语句的传入参数
(5)  执行SQL语句并获得查询结果
(6)  对查询结果进行转换处理并将处理结果返回
(7)  释放相关资源(关闭Connection,关闭Statement,关闭ResultSet)
实现JDBC有七个步骤,哪些步骤是可以进一步封装的,减少我们开发的代码量?
2.  JDBC演变到Mybatis过程
第一步优化:连接获取和释放
问题描述:通过JDBC的方式数据库连接频繁的开启和关闭本身就造成了资源的浪费,影响系统的性能;但是现在连接池多种多样,可能存在变化,有可能采用DBCP的连接池,也有可能采用容器本身的JNDI数据库连接池。
解决问题:数据库连接的获取和关闭我们可以使用数据库连接池来解决资源浪费的问题,通过连接池就可以反复利用已经建立的连接去访问数据库了,减少连接的开启和关闭的时间。
同时通过DataSource进行隔离解耦,统一从DataSource里面获取数据库连接,DataSource具体由DBCP实现还是由容器的JNDI实现都可以,将DataSource的具体实现通过让用户配置来应对变化。
第二步优化:SQL统一存取
问题描述:通过JDBC的方式,可读性很差,不利于维护以及做性能调优;改动Java代码需要重新编译、打包部署。不利于取出SQL在数据库客户端执行(取出后还得删掉中间的Java代码,编写好的SQL语句写好后还得通过+号在Java进行拼凑)。
     解决问题:把SQL语句统一集中放到配置文件,通过SQL语句的key值去获取对应的SQL语句。
第三步优化:传入参数映射和动态SQL
问题描述:很多情况下,我们都可以通过在SQL语句中设置占位符来达到使用传入参数的目的,这种方式本身就有一定局限性,它是按照一定顺序传入参数的,要与占位符一一匹配。但是,如果我们传入的参数是不确定的(比如列表查询,根据用户填写的查询条件不同,传入查询的参数也是不同的,有时是一个参数、有时可能是三个参数),那么我们就得在后台代码中自己根据请求的传入参数去拼凑相应的SQL语句,这样的话还是避免不了在Java代码里面写SQL语句的命运。
解决问题:通过专门的SQL解析器来解析SQL语句,SQL语句经过解析后就可以动态的生成符合上下文的SQL语句,同时使用#变量名#表示占位符变量,使用$变量名$表示非占位符变量
第四步优化:结果映射和结果缓存
问题描述:执行SQL语句、获取执行结果、对执行结果进行转换处理、释放相关资源是一整套下来的。假如是执行查询语句,那么执行SQL语句后,返回的是一个ResultSet结果集,这个时候我们就需要将ResultSet对象的数据取出来,不然等到释放资源时就取不到这些结果信息了。我们从前面的优化来看,以及将获取连接、设置传入参数、执行SQL语句、释放资源这些都封装起来了,只剩下结果处理这块还没有进行封装,如果能封装起来,每个数据库操作都不用自己写那么一大堆Java代码,直接调用一个封装的方法就可以搞定了
解决问题:一般对执行结果的有哪些处理,有可能将结果不做任何处理就直接返回,也有可能将结果转换成一个JavaBean对象返回、一个Map返回、一个List返回等等,结果处理可能是多种多样的。从这里看,我们必须告诉SQL处理器两点:第一,需要返回什么类型的对象;第二,需要返回的对象的数据结构怎么跟执行的结果映射,这样才能将具体的值copy到对应的数据结构上。
      接下来,我们可以进而考虑对SQL执行结果的缓存来提升性能。缓存数据都是key-value的格式,那么这个key怎么来呢?怎么保证唯一呢?即使同一条SQL语句几次访问的过程中由于传入参数的不同,得到的执行SQL语句也是不同的。那么缓存起来的时候是多对。但是SQL语句和传入参数两部分合起来可以作为数据缓存的key值。
第五步优化:解决重复SQL语句问题
问题描述:将所有SQL语句都放到配置文件中,这个时候会遇到一个SQL重复的问题,几个功能的SQL语句其实都差不多,有些可能是SELECT后面那段不同、有些可能是WHERE语句不同。有时候表结构改了,那么我们就需要改多个地方,不利于维护。
解决问题:通过将SQL片段模块化,将重复的SQL片段独立成一个SQL块,然后在各个SQL语句引用重复的SQL块,这样需要修改时只需要修改一处即可。
 3.  优化的总结
对JDBC的优化和封装:
(1) 使用数据库连接池对连接进行管理
(2) SQL语句统一存放到配置文件
(3) SQL语句变量和传入参数的映射以及动态SQL
(4) 动态SQL语句的处理
(5) 对数据库操作结果的映射和结果缓存
(6) SQL语句的重复使用
三.框架整体设计
1.       总体流程

mybatis_1  

1)加载配置并初始化
触发条件:加载配置文件
          配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
2)接收调用请求
触发条件:调用Mybatis提供的API
      传入参数:为SQL的ID和传入参数对象
处理过程:将请求传递给下层的请求处理层进行处理。
3处理操作请求
 触发条件:API接口层传递请求过来
                      传入参数:为SQL的ID和传入参数对象
                      处理过程:
      (A)根据SQL的ID查找对应的MappedStatement对象。
      (B)根据传入参数对象解析MappedStatement对象,得到最终要执行的SQL和执行传入参数。
      (C)获取数据库连接,根据得到的最终SQL语句和执行传入参数到数据库执行,并得到执行结果。
      (D)根据MappedStatement对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果。
      (E)释放连接资源。
 4)返回结果处理
   将最终的处理结果返回
 
2.       功能架构设计
  mybatis_2
把Mybatis的功能架构分为三层:
(1)API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
2)数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
(3)基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
 
 
 
3.       框架架构设计
mybatis_3
框架架构讲解:
(1)加载配置:配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
(2)SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。
(3) SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。
(4)结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者基本数据类型,并将最终结果返回。
  
 
四.Mybatis入门
每一个Mybatis应该都是以一个SqlSessionFactory实例为中心的,一个SqlSessionFactory实例都可以使用SqlSessionFactoryBuilder来创造。从配置类中创造的定制SqlSessionFactoryBuilder 实例,可以使用XML配置文件来生成一个SqlSessionFactory 实例。
1.       从xml中创造SqlSessionFactory
MyBatis 有一个Resources 通用类,类中有许多方法可以简单地从类路径和其他地址中加载资源。
String resource = "org/mybatis/example/Configuration.xml";
Reader reader = Resources.getResourceAsReader(resource);
sqlMapper = new SqlSessionFactoryBuilder().build(reader);
 
XML 文件包含了许多MyBatis 的核心设置,包括一个获取数据库连接(Connection)实例的数据源(DataSource),一个决定事务作用域和操作的TransactionManager。全部的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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/XXMapper.xml"/>
</mappers>
</configuration>
2.       不使用xml文件新建SqlSessionFactory
DataSource dataSource = DataSourceFactory.getDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment =
new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(XXMapper.class);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(configuration);
3.       使用SqlSessionFactory获取SqlSession
通过一个SqlSessionFactory,就可以获取一个SqlSession实例,SqlSession包含了针对数据库执行语句的每一个方法,直接使用SqlSession执行已经映射的每一个SQL语句:
SqlSession session = sqlSessionFactory.openSession();
try {
XXMapper mapper = session.getMapper(XXMapper.class);
XX  xx= mapper.selectXx (101);
} finally {
session.close();
五.探究SQL映射语句
接下来先看一个简单的demo,来完成SqlSession的调用过程
<?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.coin.dao.PersonDao">
         <select id="getPerson" resultType="Person" parameterType="long">
            SELECT id,name
            FROM T_Person
            WHERE id = #{id}
</select></mapper>
可以用下列语句简单地针对Mapper 接口进行调用上面文件,代码如下:
PersonDao mapper = session.getMapper(PersonDao.class);
Person p= mapper.getPerson (1);
 
还有一个关于Mapper 类的技巧。它们的映射语句完全不需要使用XML 来配置,可以使用JAVA 注解方式来取代。比如,上面的XML 语句可以替换为:
public interface PersonDao {
@Select("SELECT id,name
            FROM T_Person
            WHERE id = #{id}
")
Person  getPerson (int id);
 
总结:注解是非常简单明了的,但是JAVA 注解既有局限性,在语句比较复杂的情况下又比较容易混乱。所以,如果你的语句比较复杂,最好还是使用XML 来映射语句。
六.作用域和生命周期
1.       SqlSessionFactoryBuilder
这个类可以被初始,使用和丢弃,因为如果已经创建好一个SqlSessionFactory后就不用再保留它,所以SqlSessionFactoryBuilder的最好作用域是方法体内的。
2.       SqlSessionFactory
一旦创建,SqlSessionFactory 就会在整个应用过程中始终存在。所以没有理由去销毁和再创建它,一个应用运行中也不建议多次创建SqlSessionFactory。因此SqlSessionFactory最好的作用域是Application。可以结合使用Google Guice 或Spring 来进行依赖反射。这些框架允许你生成管理器来管理SqlSessionFactory 的单例生命周期。
3.       SqlSession
每个线程都有自己的SqlSession 实例,SqlSession 实例是不能被共享,也是不是线程安全的。因此最好使用Request 作用域或者方法体作用域。比如说在Servlet 中的HttpSession 中。如果你正在使用WEB 框架,应该让SqlSession 跟随HTTP 请求的相似作用域。也就是说,在收到一个HTTP 请求过后,打开SqlSession,等返回一个回应以后,立马关掉这个SqlSession。关闭SqlSession 是非常重要的。你必须要确保SqlSession 在finally 方法体中正常关闭。可以使用下面的标准方式来关闭:
SqlSession session = sqlSessionFactory.openSession();
try {
// do work
} finally {
session.close();
使用这种模式来贯穿你的所有代码,以确保所有数据库资源都被完全关闭。[这是假定不是使用你自己的数据库连接,而是使用MyBatis 来管理你的数据库连接资源]。
4.       Mapper实例
Mapper是一种创建用于绑定映射语句的接口。Mapper接口的实例是用SqlSession来获得的,所以Mapper 实例作用域像SqlSession 一样,使用请求作用域。在方法被调用的时候调用Mapper实例,然后使用后,就会自动销毁掉,而不需要使用明确的注销。下面例子演示如何操作:
SqlSession session = sqlSessionFactory.openSession();
try {
PersonDao mapper = session.getMapper(PersonDao.class);
// do work
} finally {
session.close();
七.Mapper的XML配置文件
Mapper的XML配置文件包含一些设置和属性,用于增强Mybatis的动作。Configuration的各个节点配置如下:
mybatis_4
1.       属性(properties)
JAVA 属性文件就可以配置直观的、可代替的属性,或者是属性项的子项。比如:
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="test"/>
<property name="password" value="test/>
</properties>
通过动态配置,这些属性都可以用替换整个文件的值。例如:
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
例子中的usrname 和password 将被属性文件中设置的值所替换, driver 和value 属性也将被config.properties 文件中值所替换,这为配置提供了多种选择。
属性值也可以设入到SqlSessionBuilder.build()方法中,例如:
SqlSessionFactory factory =
sqlSessionFactoryBuilder.build(reader, props);
// ... or ...
SqlSessionFactory factory =
sqlSessionFactoryBuilder.build(reader, environment, props);
如果一个属性项在多个地方出现,那MyBatis 将按以下顺序加载:
􀁺 属性文件中的属性项首先被读取
􀁺 在类路径或URL 资源中读取的属性项第二顺序加载,并且可以覆盖第一顺序加载的值
􀁺 在方法体中给定的参数值最后加载,覆盖上述两种加载的值。
2.       设置(settings
这是MyBatis 修改操作运行过程细节的重要的步骤。下方这个表格描述了这些设置项、含义和默认值。
mybatis_5
例如:
<settings>
       <setting name="cacheEnabled" value="true" />
       <setting name="lazyLoadingEnabled" value="true" />
       <setting name="multipleResultSetsEnabled" value="true" />
       <setting name="useColumnLabel" value="true" />
       <setting name="useGeneratedKeys" value="false" />
       <setting name="enhancementEnabled" value="false" />
       <setting name="defaultExecutorType" value="SIMPLE" />
<setting name="defaultStatementTimeout" value="15000"/>
</settings>
3.       类型别名(typeAliases
类型别名是Java 类型的简称。它仅仅只是关联到XML 配置,简写冗长的JAVA 类名。例如:
<typeAliases>
<typeAlias alias="Person"   type="com.coin.model.Person "/>
</typeAliases>
<typeAliases>
使用这个配置,“Person”就能在任何地方代替“com.coin.model.Person”被使用。
对于普通的Java类型,有许多内建的类型别名。它们都是大小写不敏感的,由于重载的名字,要注意原生类型的特殊处理
 

别名
映射的类型
_byte
byte
_long
long
_short
short
_int
int
_integer
int
_double
double
_float
float
_boolean
boolean
string
String
byte
Byte
long
Long
short
Short
int
Integer
integer
Integer
double
Double
float
Float
boolean
Boolean
date
Date
decimal
BigDecimal
bigdecimal
BigDecimal
object
Object
map
Map
hashmap
HashMap
list
List
arraylist
ArrayList
collection
Collection
iterator
Iterator

 
4.       类型句柄(typeHandlers
无论是MyBatis在预处理语句中设置一个参数,还是从结果集中取出一个值时,类型处理器被用来将获取的值以合适的方式转换成Java类型。下面这个表格描述了默认的类型处理器。

类型处理器
Java类型
JDBC类型
BooleanTypeHandler
Boolean,boolean
任何兼容的布尔值
ByteTypeHandler
Byte,byte
任何兼容的数字或字节类型
ShortTypeHandler
Short,short
任何兼容的数字或短整型
IntegerTypeHandler
Integer,int
任何兼容的数字和整型
LongTypeHandler
Long,long
任何兼容的数字或长整型
FloatTypeHandler
Float,float
任何兼容的数字或单精度浮点型
DoubleTypeHandler
Double,double
任何兼容的数字或双精度浮点型
BigDecimalTypeHandler
BigDecimal
任何兼容的数字或十进制小数类型
StringTypeHandler
String
CHAR和VARCHAR类型
ClobTypeHandler
String
CLOB和LONGVARCHAR类型
NStringTypeHandler
String
NVARCHAR和NCHAR类型
NClobTypeHandler
String
NCLOB类型
ByteArrayTypeHandler
byte[]
任何兼容的字节流类型
BlobTypeHandler
byte[]
BLOB和LONGVARBINARY类型
DateTypeHandler
Date(java.util)
TIMESTAMP类型
DateOnlyTypeHandler
Date(java.util)
DATE类型
TimeOnlyTypeHandler
Date(java.util)
TIME类型
SqlTimestampTypeHandler
Timestamp(java.sql)
TIMESTAMP类型
SqlDateTypeHandler
Date(java.sql)
DATE类型
SqlTimeTypeHandler
Time(java.sql)
TIME类型
ObjectTypeHandler
Any
其他或未指定类型
EnumTypeHandler
Enumeration类型
VARCHAR-任何兼容的字符串类型,作为代码存储(而不是索引)。

 
 
可以重写(loverride)类型句柄或者是创建你自己的方式来处理不支持或者是非标准的类型。只需要简单地实现org.mybatis.type 包里的TypeHandler,并且映射你的新类型句柄类到一个JAVA 类型,再选定一个JDBC 类型。例如:
public class ExampleTypeHandler implements TypeHandler {
public void setParameter(
PreparedStatement ps, int i, Object parameter,JdbcType jdbcType)
throws SQLException {
  System.out.println("setParameter - parameter: " + ((String) parameter) + ", jdbcType: " + jdbcType.TYPE_CODE);  
ps.setString(i, (String) parameter);
} public Object getResult(
ResultSet rs, String columnName)
throws SQLException {
return rs.getString(columnName);
} public Object getResult(
CallableStatement cs, int columnIndex)
throws SQLException {
return cs.getString(columnIndex);
}
}
 
// MapperConfig.xml
<typeHandlers>
<typeHandler javaType="String" jdbcType="VARCHAR"
handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>
这样的类型句柄,将会覆盖现有的处理JAVA String 属性与VARCHAR 和返回值的类型句柄。注意,MyBatis 无法省查数据库的元数据从而决定类型,所以你必须指定参数它是一个VARCHAR 类型,并且结果映射到正确的类型句柄上。这么做主要是由于MyBatis 在没有执行语句之类,无法得知数据的类型。
5.       对象工厂(ObjectFactory
每次MyBatis 为结果对象创建一个新实例,都会用到ObjectFactory。默认的ObjectFactory 与使用目标类的构造函数创建一个实例毫无区别,如果有已经映射的参数,那也可能使用带参数的构造函数。
如果你重写ObjectFactory 的默认操作,你可以通过继承org.apache.ibatis.reflection.factory.DefaultObjectFactory创建一下你自己的。
ObjectFactory接口很简单。它包含两个创建用的方法,一个是处理默认构造方法的,另外一个是处理带参数构造方法的。最终,setProperties方法可以被用来配置ObjectFactory。在初始化你的ObjectFactory实例后,objectFactory元素体中定义的属性会被传递给setProperties方法。
 
public class ExampleObjectFactory extends DefaultObjectFactory {
public Object create(Class type) {
return super.create(type);
}
 public Object create(
Class type,
List<Class> constructorArgTypes,
List<Object> constructorArgs) {
return super.create(type, constructorArgTypes, constructorArg
}
public void setProperties(Properties properties) {
super.setProperties(properties);
}
}
 
// MapperConfig.xml
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
<property name="someProperty" value="100"/>
</objectFactory>
6.       插件(plugins
MyBatis允许在某一点拦截已映射语句执行的调用。默认情况下,MyBatis允许使用插件来拦截方法调用:
·         Executor(update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
·         ParameterHandler(getParameterObject, setParameters)
·         ResultSetHandler(handleResultSets, handleOutputParameters)
·         StatementHandler(prepare, parameterize, batch, update, query)
 
这些类中方法的详情可以通过查看每个方法的签名来发现,而且它们的源代码在MyBatis的发行包中有。应该理解你覆盖方法的行为,假设你所做的要比监视调用要多。如果你尝试修改或覆盖一个给定的方法,你可能会打破MyBatis的核心。这是低层次的类和方法,要谨慎使用插件。
使用插件是它们提供的非常简单的力量。简单实现拦截器接口,要确定你想拦截的指定签名。
@Intercepts({@Signature(
type= Executor.class
method = "update"
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
 public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
}
// MapperConfig.xml
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugins>
上面的这个插件可以在执行器上拦截所有“update”方法的调用,这里的执行器,是一个映射语句内部对象的深架构的执行器。
7.       环境(environments
MyBatis 可以配置多个环境。这样可以帮助你SQL 映射对应多种数据库。比如说,你想为开发、测试、发布产品配置不用的环境。或者,你想为多个数据库产品共享相同的模式,或者也想使用相同的SQL映射。虽然你可以配置多重环境,但是也可以只选择一对一SqlSessionFactory实例。
这样想要指定生成哪个环境,只要简单地把它做了一个可选参数代入SqlSessionFactoryBuilder。下面两种方式都可以:
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment,properties);
如果环境变更省略了,就会载入默认的环境变量。像这样:
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader,properties);
环境元素定义这些环境是如何被配置的。
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="" value=""/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
注意这些关键段:
􀁺 设定一个默认环境ID
􀁺 这个环境ID 对每个环境都起作用
􀁺 配置事务管理器(MyBatis 有两个事务管理类型:JDBC,MANAGED)
􀁺 配置数据源(数据源类型有三种:UNPOOLED,POOLED,JNDI)
8.       映射器(Mappers
这里是告诉MyBatis 去哪寻找映射SQL 的语句。可以使用类路径中的资源引用,或者使用字符,输入确切的URL 引用。
例如:
    <mappers>
        <mapper   resource="com/coin /dao/personDao.xml"/>
   </mappers>
八.SQL映射语句文件
Mybatis真正强大的地方就是在映射语句,SQL映射XML文件包含一些的元素:
􀁺 cache – 配置给定模式的缓存
􀁺 cache-ref – 从别的模式中引用一个缓存
􀁺 resultMap – 这是最复杂而却强大的一个元素了,它描述如何从结果集中加载对象
􀁺 sql – 一个可以被其他语句复用的SQL 块
􀁺 select – 映射SELECT 语句
􀁺 insert – 映射INSERT 语句
􀁺 update – 映射UPDATE 语句
􀁺 delete – 映射DELEETE 语句
1.       Select
一个Select元素很简单,例如:
           <select id="getPerson" resultType="Person" parameterType="long">
            SELECT ID,Name
            FROM T_PERSON
            WHERE id = #{id}
           
  </select>
这条语句就叫做‘getAllPersons,有一个long参数,并返回一个Person类型的对象。参数的标识是:#{ id }。
 
select 语句属性配置细节:

属性
描述
取值
默认
id
在这个模式下唯一的标识符,可被其它语句引用
parameterType
传给此语句的参数的完整类名或别名
resultType
语句返回值类型的整类名或别名。注意,如果是集合,那么这里填写的是集合的项的整类名或别名,而不是集合本身的类名。(resultType 与resultMap 不能并用)
resultMap
引用的外部resultMap 名。结果集映射是MyBatis 中最强大的特性。许多复杂的映射都可以轻松解决。(resultType 与resultMap 不能并用)
flushCache
如果设为true,则会在每次语句调用的时候就会清空缓存。select 语句默认设为false
true|false
false
useCache
如果设为true,则语句的结果集将被缓存。select 语句默认设为false true|false false
timeout 设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定
true|false
false
timeout 
设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定
正整数
未设置
fetchSize
设置一个值后,驱动器会在结果集数目达到此数值后,激发返回,默认为不设值,由驱动器自己决定
正整数
驱动器决定
statementType
statement,preparedstatement,callablestatement。
预准备语句、可调用语句
STATEMENT
PREPARED
CALLABLE
PREPARED
resultSetType
forward_only,scroll_sensitive,scroll_insensitive
只转发,滚动敏感,不区分大小写的滚动
FORWARD_ONLY
SCROLL_SENSITIVE
SCROLL_INSENSITIVE
驱动器决定

 
 
2.       insert
insert可以使用数据库支持的自动生成主键策略,设置useGeneratedKeys=”true”,然后把keyProperty 设成对应的列,就搞定了。
 还可以使用selectKey元素。下面例子,使用mysql数据库nextval(\\'student\\')为自定义函数,用来生成一个key。
下面一个例子是在oracle数据库里面的:
<insert id="createPerson" parameterType="Person">
    INSERT INTO …VALUES (#{id},#{name}
</insert>
insert语句属性配置细节:

属性
描述
取值
默认
id
在这个模式下唯一的标识符,可被其它语句引用
parameterType
传给此语句的参数的完整类名或别名
flushCache
如果设为true,则会在每次语句调用的时候就会清空缓存。select 语句默认设为false
true|false
false
useCache
如果设为true,则语句的结果集将被缓存。select 语句默认设为false true|false false
timeout 设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定
true|false
false
timeout 
设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定
正整数
未设置
fetchSize
设置一个值后,驱动器会在结果集数目达到此数值后,激发返回,默认为不设值,由驱动器自己决定
正整数
驱动器决定
statementType
statement,preparedstatement,callablestatement。
预准备语句、可调用语句
STATEMENT
PREPARED
CALLABLE
PREPARED
useGeneratedKeys
告诉MyBatis 使用JDBC 的getGeneratedKeys 方法来获取数据库自己生成的主键(MySQL、SQLSERVER 等
关系型数据库会有自动生成的字段)。默认:false
true|false
false
keyProperty
标识一个将要被MyBatis 设置进getGeneratedKeys 的key 所返回的值,或者为insert 语句使用一个selectKey
子元素。

selectKey语句属性配置细节:

属性
描述
取值
keyProperty
selectKey 语句生成结果需要设置的属性。
resultType
生成结果类型,MyBatis 允许使用基本的数据类型,包括String 、int类型。
order
可以设成BEFORE 或者AFTER,如果设为BEFORE,那它会先选择主键,然后设置keyProperty,再执行insert语句;如果设为AFTER,它就先运行insert 语句再运行selectKey 语句,通常是insert 语句中内部调用数据库(像Oracle)内嵌的序列机制。 
BEFORE
AFTER
statementType
像上面的那样, MyBatis 支持STATEMENT,PREPARED和CALLABLE 的语句形式, 对应Statement ,PreparedStatement 和CallableStatement 响应
STATEMENT
PREPARED
CALLABLE

 
3.       update,delete
update,delete的例子如下:
    <update id="updatePerson" parameterType="Person">
            UPDATE T_PERSON
            SET NAME = #{name},    
            WHERE ID = #{id}
    </update>
 
    <delete id="deletePerson" parameterType="long">
            DELETE T_PERSON
            WHERE ID = #{value}
</delete>
update、delete语句属性配置细节:

属性
描述
取值
默认
id
在这个模式下唯一的标识符,可被其它语句引用
parameterType
传给此语句的参数的完整类名或别名
flushCache
如果设为true,则会在每次语句调用的时候就会清空缓存。select 语句默认设为false
true|false
false
useCache
如果设为true,则语句的结果集将被缓存。select 语句默认设为false true|false false
timeout 设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定
true|false
false
timeout 
设置驱动器在抛出异常前等待回应的最长时间,默认为不设值,由驱动器自己决定
正整数
未设置
fetchSize
设置一个值后,驱动器会在结果集数目达到此数值后,激发返回,默认为不设值,由驱动器自己决定
正整数
驱动器决定
statementType
statement,preparedstatement,callablestatement。
预准备语句、可调用语句
STATEMENT
PREPARED
CALLABLE
PREPARED

 
4.       SQL
Sql元素用来定义一个可以复用的SQL 语句段,供其它语句调用。比如:
<sql id="columns">ID id,NAME name</sql>
这个语句块,可以包含到别的语句中,比如:
<select id="getAllPersons" resultType="Person">
        SELECT <include refid="columns"/>
            FROM T_PERSON
    </select>
5.       参数(parameters
 MyBatis的参数可以使用的基本数据类型和Java的复杂数据类型。
        基本数据类型,String,int,date等。
        但是使用基本数据类型,只能提供一个参数,所以需要使用Java实体类,或Map类型做参数类型。通过#{}可以直接得到其属性。
5.1  基本类型参数
<select id="getPerson" resultType="Person" parameterType="long">
            SELECT ID id,NAME name
            FROM T_PERSON
            WHERE ID = #{id}
      </select>
5.2  Java实体类型参数
          <insert id="createPerson" parameterType="Person">
                    INSERT INTO T_PERSON(ID,NAME)
            VALUES (#{id},#{name}
)
</insert>
多参数的实现
如果想传入多个参数,则需要在接口的参数上添加@Param注解。给出一个实例:
<!-- 查询学生list,like姓名、=性别,多参数方式 -->
    <select id="getStudentListWhereParam" resultMap="studentResultMap">
        SELECT * from student ST
        <where>
            <if test="name!=null and name!=\\'\\' ">
                ST.name LIKE CONCAT(CONCAT(\\'%\\', #{name}),\\'%\\')
            </if>
            <if test="gender!= null and gender!= \\'\\' ">
                AND ST.gender = #{gender}
            </if>
        </where>
    </select>
5.3  字符串代入法
默认的情况下,使用#{}语法会促使MyBatis 生成PreparedStatement 属性并且使用PreparedStatement 的参数(=?)来安全的设置值。尽量这些是快捷安全,也是经常使用的。但有时候你可能想直接未更改的字符串代入到SQL 语句中。比如说,对于ORDER BY,你可能会这样使用:ORDER BY ${columnName}但MyBatis 不会修改和规避掉这个字符串。
        注意:这样地接收和应用一个用户输入到未更改的语句中,是非常不安全的。这会让用户能植入破坏代码,所以,要么要求字段不要允许客户输入,要么你直接来检测他的合法性 。
6.       resultMap
6.1  一对一
 
<resultMap id="studentTeacherResultMap" type="student">
           <id property="id" column="id"/>
          <result property="name" column="name"/>
          <result property="gender" column="gender"/>
          <result property="major" column="major"/>  
          <result property="grade" column="grade"/>
           
          <!--使用resultMap属性引用下面的教师实体映射-->
          <association property="supervisor" javaType="teacher"
          resultMap="com.coin.dao.teacher.TeacherDao.supervisorResultMap"/>
      </resultMap>
      
      
       <select id="getById" parameterType="int" resultMap="studentTeacherResultMap">
          select st.id,st.name,st.gender, 
          st.major,st.grade, 
          <!--为教师的id取别名,避免MyBatis向教师实体注入 
          此属性时与学生id混淆。以下的name和gender等属性 
          也是如此-->
          t.id t_id,t.name t_name,t.gender t_gender, 
          t.title,t.research_area 
          from student st, teacher t 
          where st.supervisor_id = t.id 
          and st.id=#{id} 
   </select>
 
6.2    一对多
 
     <!--TeacherMapper接口中getById方法对应的SQL语句。 
         查询教师及其指导的学生的信息。由于教师、学生都有 
         id、name、gender等属性,因此给教师的字段都起了别名-->
         <select id="getById" parameterType="int"  resultMap="supervisorResultMap">
          select t.id t_id, t.name t_name, t.gender t_gender, 
          t.research_area t_research_area, t.title t_title, 
          s.id,s.name, s.gender,s.major,s.grade 
          from teacher t,student s where t.id=#{id} 
          and s.supervisor_id = t.id 
         </select>
 
         <!--教师实体映射-->
         <resultMap id="supervisorResultMap" type="teacher">
            <id property="id" column="t_id"/>
            <result property="name" column="t_name"/>
            <result property="gender" column="t_gender"/>
            <result property="researchArea" column="t_research_area"/>  
            <result property="title" column="t_title"/>
            <!--collection元素映射教师的指导学生集合的属性。resultMap 
            以命名空间名.resultMap的id的形式,引用studentResultMap。 
            需要注意的是,上面的select语句中学生的字段名/别名应与 
            studentResultMap中的column属性一致-->
            <collection property="supStudents"  
              resultMap="com.coin.dao.student.StudentDao.studentTeacherResultMap"/>
         </resultMap>
九.cache 缓存
默认的情况,缓存是没有开启,除了会话缓存以外,它可以提高性能,且能解决全局依赖。开启二级缓存,只需要在SQL 映射文件中加入简单的一行:<cache/>
这句简单的语句的作用如下:
1. 所有在映射文件里的select 语句都将被缓存。
2. 所有在映射文件里insert,update 和delete 语句会清空缓存。
3. 缓存使用“最近很少使用”算法来回收
4. 缓存不会被设定的时间所清空。
5. 每个缓存可以存储1024 个列表或对象的引用(不管查询出来的结果是什么)。
6. 缓存将作为“读/写”缓存,意味着获取的对象不是共享的且对调用者是安全的。不会有其它的调用 者或线程潜在修改。
可以采用第三方缓存框架缓存,例如:OSCache,EHCache,Hazelcast
<cache type="org.mybatis.caches.oscache.OSCache"/>
也可以使用cache-ref 来引用另外一个缓存。
<cache-ref namespace=”com.someone.application.data.SomeMapper”/>
十.动态语句
1.       if
动态SQL中最常用的事情就是用条件地包含一个where字句。比如:
 
<select id="getStudentListWhereParam" resultMap="studentResultMap">
        SELECT * from student ST
        <where>
            <if test="name!=null and name!=\\'\\' ">
                ST.name LIKE CONCAT(CONCAT(\\'%\\', #{name}),\\'%\\')
            </if>
            <if test="gender!= null and gender!= \\'\\' ">
                AND ST.gender = #{gender}
            </if>
        </where>
    </select>
2.       choose,when,otherwise
有时候我们并不想应用所有的条件,而只是想从多个选项中选择一个。类似于Java 的switch 语句,MyBatis提供了choose 元素。
      <select id="findStudentLike"  resultMap="studentResultMap">
            SELECT * FROM student ST
            <where>
               <choose>
                <when test="name != null">
                    ST.name LIKE CONCAT(CONCAT(\\'%\\', #{name}),\\'%\\')
                </when>
                <when test="major != null">
                    ST.name LIKE CONCAT(CONCAT(\\'%\\', #{major}),\\'%\\')
                </when>
                <otherwise>
                   AND ST.grade = \\'2012\\'
                </otherwise>
              </choose>
        </where>
</select>
3.       set,trim
3.1  set
set元素被用做动态囊括列名来更新,而忽略其它的。
当在update语句中使用if标签时,如果前面的if没有执行,则或导致逗号多余错误。使用set标签可以将动态的配置SET 关键字,和剔除追加到条件末尾的任何不相关的逗号。使用if+set标签修改后,如果某项为null则不进行更新,而是保持数据库原值。如下示例:
 
       <!--  if/set(判断参数) - 将实体类不为空的属性更新 -->
    <update id="updateStudent_if_set" parameterType="student">
        update student ST
        <set>
            <if test="name!= null and name != \\'\\'">
                ST.name = #{name},
            </if>
            <if test="gender != null and gender != \\'\\'">
                ST.gender = #{gender},
            </if>
            <if test="major!= null and major != \\'\\'">
                ST.major = #{major},
            </if>
            <if test="grade!= null and grade!= \\'\\'">
                ST.grade = #{grade}
            </if>
        </set>
        WHERE ST.id = #{id}
</update>
3.1  trim
trim是灵活的去除多余关键字的标签,它可以实现where和set的效果
trim等同where效果的例子:
<select id="getStudentListTrimParam" resultMap="studentResultMap">
        SELECT * from student ST
        <trim  prefix="WHERE" prefixOverrides="AND|OR">
            <if test="name!=null and name!=\\'\\' ">
                ST.name LIKE CONCAT(CONCAT(\\'%\\', #{name}),\\'%\\')
            </if>
            <if test="gender!= null and gender!= \\'\\' ">
                AND ST.gender = #{gender}
            </if>
        </trim>
     </select>
trim等同set效果的例子:
<!-- if/trim(判断参数) - 将实体类不为空的属性更新 -->
    <update id="updateStudent_if_trim" parameterType="student">
        update student ST
        <trim prefix="SET" suffixOverrides=",">
            <if test="name!= null and name != \\'\\'">
                ST.name = #{name},
            </if>
            <if test="gender != null and gender != \\'\\'">
                ST.gender = #{gender},
            </if>
            <if test="major!= null and major != \\'\\'">
                ST.major = #{major},
            </if>
            <if test="grade!= null and grade!= \\'\\'">
                ST.grade = #{grade}
            </if>
        </trim>
        WHERE ST.id = #{id}
</update>
4.       foreach
 另外一个对于动态SQL 非常必须的,主是要迭代一个集合,通常是用于IN 条件。
4.1  传入List
      <select id="forEachStudentList"  resultMap="studentResultMap">
        SELECT * FROM student ST 
        WHERE ST.id in
        <foreach collection="list" item="item" index="index" open="("  separator=","  close=")">  
             #{item}   
        </foreach>
    </select>
 4.2  传入Array
<select id="forEachStudentArray"  resultMap="studentResultMap">
        SELECT * FROM student ST
        WHERE ST.id in
        <foreach collection="array" item="item" index="index"  open="("  separator=","  close=")">  
             #{item}   
         </foreach>
  </select>
 

关于Coin163网站地图

Copyright 2012-2013 Coin163.com ( Coin163 ) All Rights Reserved