四.mybatis主配置文件
mybatis_config.xml文件是mybatis框架的主配置文件,用于配置mybatis数据源及插件信息
在MyBatis框架的核心配置文件中,<configuration>元素是配置文件的根元素,其他元素都要在<configuration>元素内配置。在上一章的入门案例中,我们在配置文件内只使用了<environments>和<mapper>等几个元素,但在实际开发时,通常还会对其他一些元素进行配置。
MyBatis配置文件中的主要元素如图1所示。
图1 MyBatis配置文件中的主要元素
从图1中可以看到,在MyBatis的配置文件中包含了多个元素,这些元素在配置文件中分别发挥着不同的作用。开发人员所需要熟悉的就是图中<configuration>元素各个子元素的配置。
注意:
<configuration>的子元素必须按照图7-1中由上到下的顺序进行配置,否则MyBatis在解析XML配置文件的时候会报错。
1.<properties>
<properties>用于引用属性文件,在内部可以使用<property>子标签设置键值对
<properties>是一个配置属性的元素,该元素通常用来将内部的配置外在化,即通过外部的配置来动态的替换内部定义的属性。例如,数据库的连接等属性,就可以通过典型的Java属性文件中的配置来替换,具体方式如下:
(1)在项目的src目录下,添加一个全名为db.properties的配置文件,编辑后的代码如下所示。
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root
(2)在MyBatis配置文件mybatis-config.xml中配置<properties... />属性,具体如下。
<properties resource="db.properties" />
(3)修改配置文件中数据库连接的信息,具体如下。
<dataSource type="POOLED">
<!-- 数据库驱动 -->
<property name="driver" value="${jdbc.driver}" />
<!-- 连接数据库的url -->
<property name="url" value="${jdbc.url}" />
<!-- 连接数据库的用户名 -->
<property name="username" value="${jdbc.username}" />
<!-- 连接数据库的密码 -->
<property name="password" value="${jdbc.password}" />
</dataSource>
完成上述配置后,dataSource中连接数据库的4个属性(driver、url、username和password)值将会由db.properties文件中对应的值来动态替换。这样就为配置提供了诸多灵活的选择。
除了可以像上述通过外部配置文件来定义属性值外,还可以通过配置<properties>元素的子元素<property>,以及通过方法参数传递的方式来获取属性值。由于使用properties配置文件来配置属性值可以方便地在多个配置文件中使用这些属性值,并且方便日后的维护和修改,所以在实际开发中,使用properties配置文件来配置属性值是最常用的方式。
2.<settings>
<settings>元素主要用于改变MyBatis运行时的行为,例如开启二级缓存、开启延迟加载等。虽然不配置<settings>元素,也可以正常运行MyBatis,但是熟悉<settings>的配置内容以及它们的作用还是十分必要的。
<settings>元素中的常见配置及其描述如表1所示。
表1 <settings>元素中的常见配置
设置参数 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 该配置影响所有映射器中配置的缓存全局开关。 | true|false | true |
lazyLoadingEnabled | 延迟加载的全局开关。开启时,所有关联对象都会延迟加载。特定关联关系中可以通过设置fetchType属性来覆盖该项的开关状态。 | true|false | false |
aggressiveLazyLoading | 关联对象属性的延迟加载开关。当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性会按需加载。 | true|false | true |
multipleResultSetsEnabled | 是否允许单一语句返回多结果集(需要兼容驱动)。 | true|false | true |
useColumnLabel | 使用列标签代替列名。不同的驱动在这方面有不同的表现。具体可参考驱动文档或通过测试两种模式来观察所用驱动的行为。 | true|false | true |
useGeneratedKeys | 允许JDBC支持自动生成主键,需要驱动兼容。如果设置为true,则这个设置强制使用自动生成主键,尽管一些驱动不兼容但仍可正常工作。 | true|false | false |
autoMappingBehavior | 指定MyBatis应如何自动映射列到字段或属性。NONE表示取消自动映射; PARTIAL只会自动映射没有定义嵌套结果集映射的结果集; FULL会自动映射任意复杂的结果集(无论是否嵌套)。 | NONE、 PARTIAL、 FULL | PARTIAL |
defaultExecutorType | 配置默认的执行器。SIMPLE就是普通的执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新。 | SIMPLE、 REUSE、 BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定驱动等待数据库响应的秒数。当没有设置的时候,它取的就是驱动默认的时间。 | 任何正整数 | 没有设置 |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则(camel case)映射 | true|false | false |
jdbcTypeForNull | 当没有为参数提供特定的JDBC类型时,为空值指定JDBC类型。某些驱动需要指定列的JDBC类型,多数情况直接用一般类型即可,比如NULL、VARCHAR或OTHER。 | NULL、 VARCHAR、 OTHER | OTHER |
表1中介绍了<settings>元素中的常见配置,这些配置在配置文件中的使用方式如下:
<!-- 设置 -->
<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="autoMappingBehavior" value="PARTIAL" />
...
</settings>
上面所介绍的配置内容大多数都不需要开发人员去配置它,通常在需要时只配置少数几项即可。这里读者只需要了解这些可设置的参数值及其含义即可。
3.<typeAliases>
<typeAliases>元素用于为配置文件中的Java类型设置一个简短的名字,即设置别名。别名的设置与XML配置相关,其使用的意义在于减少全限定类名的冗余。
使用<typeAliases>元素配置别名的方法如下:
<!-- 定义别名 -->
<typeAliases>
<typeAlias alias="user" type="com.itheima.po.User"/>
</typeAliases>
上述示例中,<typeAliases>元素的子元素<typeAlias>中的type属性用于指定需要被定义别名的类的全限定名;alias属性的属性值user就是自定义的别名,它可以代替com.itheima.po.User使用在MyBatis文件的任何位置。如果省略alias属性,MyBatis会默认将类名首字母小写后的名称作为别名。
当POJO类过多时,还可以通过自动扫描包的形式自定义别名,具体示例如下:
<!-- 使用自动扫描包来定义别名 -->
<typeAliases>
<package name="com.itheima.po"/>
</typeAliases>
上述示例中,<typeAliases>元素的子元素<package>中的name属性用于指定要被定义别名的包,MyBatis会将所有com.itheima.po包中的POJO类以首字母小写的非限定类名来作为它的别名,比如com.itheima.po.User的别名为user,com.itheima.po.Customer的别名为customer等。
需要注意的是,上述方式的别名只适用于没有使用注解的情况。如果在程序中使用了注解,则别名为其注解的值,具体如下:
@Alias(value = "user")
public class User {
//User的属性和方法
...
}
除了可以使用<typeAliases>元素自定义别名外,MyBatis框架还默认为许多常见的Java类型(如数值、字符串、日期和集合等)提供了相应的类型别名,如表1所示。
表1 MyBatis默认别名
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | Sring |
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 |
表1所列举的别名可以在MyBatis中直接使用,但由于别名不区分大小写,所以在使用时要注意重复定义的覆盖问题。
4.<typeHandler>元素
MyBatis在预处理语句(PreparedStatement)中设置一个参数或者从结果集(ResultSet)中取出一个值时,都会用其框架内部注册了的typeHandler(类型处理器)进行相关处理。typeHandler的作用就是将预处理语句中传入的参数从javaType(Java类型)转换为jdbcType(JDBC类型),或者从数据库取出结果时将jdbcType转换为javaType。
为了方便转换,MyBatis框架提供了一些默认的类型处理器,其常用的类型处理器如表1所示。
表1 常用的类型处理器
类型处理器 | Java**类型** | JDBC**类型** |
---|---|---|
BooleanTypeHandler | java.lang.Boolean,boolean | 数据库兼容的BOOLEAN |
ByteTypeHandler | java.lang.Byte,byte | 数据库兼容的NUMERIC或BYTE |
ShortTypeHandler | java.lang.Short,short | 数据库兼容的NUMERIC或SHORT INTEGER |
IntegerTypeHandler | java.lang.Integer,int | 数据库兼容的NUMERIC或INTEGER |
LongTypeHandler | java.lang.Long,long | 数据库兼容的NUMERIC或LONG INTEGE |
FloatTypeHandler | java.lang.Float,float | 数据库兼容的NUMERIC或FLOAT |
DoubleTypeHandler | java.lang.Double,double | 数据库兼容的NUMERIC或DOUBLE |
BigDecimalTypeHandler | java.math.BigDecimal | 数据库兼容的NUMERIC或DECIMAL |
StringTypeHandler | java.lang.String | CHAR,VARCHAR |
ClobTypeHandler | java.lang.String | CLOB,LONGVARCHAR |
ByteArrayTypeHandler | byte[] | 数据库兼容的字节流类型 |
BlobTypeHandler | byte[] | BLOB,LONGVARBINARY |
DateTypeHandler | java.util.Date | TIMESTAMP |
SqlTimestampTypeHandler | java.sql.Timestamp | TIMESTAMP |
SqlDateTypeHandler | java.sql.Date | DATE |
SqlTimeTypeHandler | java.sql.Time | TIME |
当MyBatis框架所提供的这些类型处理器不能够满足需求时,还可以通过自定义的方式对类型处理器进行扩展(自定义类型处理器可以通过实现TypeHandler接口或者继承BaseTypeHandle类来定义)。<typeHandler>元素就是用来在配置文件中注册自定义的类型处理器的。它的使用方式有两种,具体如下:
1.注册一个类的类型处理器
<typeHandlers>
<!-- 以单个类的形式配置 -->
<typeHandler handler="com.itheima.type.CustomtypeHandler" />
</typeHandlers>
上述代码中,子元素<typeHandler>的handler属性用于指定在程序中自定义的类型处理器类。
2.注册一个包中所有的类型处理器
<typeHandlers>
<!-- 注册一个包中所有的typeHandler,系统在启动时会自动扫描包下的所有文件-->
<package name="com.itheima.type" />
</typeHandlers>
上述代码中,子元素<package>的name属性用于指定类型处理器所在的包名,使用此种方式后,系统会在启动时自动的扫描com.itheima.type包下所有的文件,并把它们作为类型处理器。
5.<objectFactory>元素
MyBatis框架每次创建结果对象的新实例时,都会使用一个对象工厂(ObjectFactory)的实例来完成。MyBatis中默认的ObjectFactory的作用就是实例化目标类,它既可以通过默认构造方法实例化,也可以在参数映射存在的时候通过参数构造方法来实例化。
通常情况下,我们使用默认的ObjectFactory即可,MyBatis中默认的ObjectFactory是由org.apache.ibatis.reflection.factory.DefaultObjectFactory来提供服务的。大部分场景下都不用配置和修改,但如果想覆盖ObjectFactory的默认行为,则可以通过自定义ObjectFactory来实现,具体方式如下。
(1)自定义一个对象工厂。自定义的对象工厂需要实现ObjectFactory接口,或者继承DefaultObjectFactory类。由于DefaultObjectFactory类已经实现了ObjectFactory接口,所以通过继承DefaultObjectFactory类实现即可,示例代码如下所示。
// 自定义工厂类
public class MyObjectFactory extends DefaultObjectFactory {
private static final long serialVersionUID = -4114845625429965832L;
public <T> T create(Class<T> type) {
return super.create(type);
}
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes,
List<Object> constructorArgs) {
return super.create(type, constructorArgTypes, constructorArgs);
}
public void setProperties(Properties properties) {
super.setProperties(properties);
}
public <T> boolean isCollection(Class<T> type) {
return Collection.class.isAssignableFrom(type);
}
}
(2)在配置文件中使用<objectFactory>元素配置自定义的ObjectFactory,如下所示。
<objectFactory type="com.itheima.factory.MyObjectFactory">
<property name="name" value="MyObjectFactory"/>
</objectFactory>
由于自定义ObjectFactory在实际开发时不经常使用,这里读者只需要了解即可。
6.<plugins>元素
MyBatis允许在已映射语句执行过程中的某一点进行拦截调用,这种拦截调用是通过插件来实现的。<plugins>元素的作用就是配置用户所开发的插件。如果用户想要进行插件开发,必须要先了解其内部运行原理,因为在试图修改或重写已有方法的行为时,很可能会破坏MyBatis原有的核心模块。
7.<environments>元素
在配置文件中,<environments>元素用于对环境进行配置。MyBatis的环境配置实际上就是数据源的配置,我们可以通过<environments>元素配置多种数据源,即配置多种数据库。
使用<environments>元素进行环境配置的示例如下:
<environments default="development">
<environment id="development">
<!-- 使用JDBC事务管理 -->
<transactionManager type="JDBC" />
<!-- 配置数据源 -->
<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>
在上述示例代码中,<environments>元素是环境配置的根元素,它包含一个default属性,该属性用于指定默认的环境ID。<environment>是<environments>元素的子元素,它可以定义多个,其id属性用于表示所定义环境的ID值。在<environment>元素内,包含事务管理和数据源的配置信息,其中<transactionManager>元素用于配置事务管理,它的type属性用于指定事务管理的方式,即使用哪种事务管理器;<dataSource>元素用于配置数据源,它的type属性用于指定使用哪种数据源。
在MyBatis中,可以配置两种类型的事务管理器,分别是JDBC和MANAGED。关于这两个事务管理器的描述如下:
● JDBC:此配置直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务的作用域。
● MANAGED:此配置从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。默认情况下,它会关闭连接,但一些容器并不希望这样,为此可以将closeConnection属性设置为false来阻止它默认的关闭行为。
注意:
如果项目中使用的是Spring+ MyBatis,则没有必要在MyBatis中配置事务管理器,因为实际开发中,会使用Spring自带的管理器来实现事务管理。
对于数据源的配置,MyBatis框架提供了UNPOOLED、POOLED和JNDI三种数据源类型,具体如下:
1、UNPOOLED
配置此数据源类型后,在每次被请求时会打开和关闭连接。它对没有性能要求的简单应用程序是一个很好的选择。
UNPOOLED类型的数据源需要配置5种属性,如表1所示。
表1 UNPOOLED数据源需要配置的属性
属性 | 说明 |
---|---|
driver | JDBC驱动的Java类的完全限定名(并不是JDBC驱动中可能包含的数据源类) |
url | 数据库的URL地址 |
username | 登录数据库的用户名 |
password | 登录数据库的密码 |
defaultTransactionIsolationLevel | 默认的连接事务隔离级别 |
2、POOLED
此数据源利用“池”的概念将JDBC连接对象组织起来,避免了在创建新的连接实例时所需要初始化和认证的时间。这种方式使得并发Web应用可以快速的响应请求,是当前流行的处理方式(本书中使用的就是此种方式)。
配置此数据源类型时,除了表7-4中的5种属性外,还可以配置更多的属性,如表2所示。
表2 POOLED数据源可额外配置的属性
属性 | 说明 |
---|---|
poolMaximumActiveConnections | 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10。 |
poolMaximumIdleConnections | 任意时间可能存在的空闲连接数。 |
poolMaximumCheckoutTime | 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒,即20秒。 |
poolTimeToWait | 如果获取连接花费的时间较长,它会给连接池打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直处于无提示的失败),默认值:20000毫秒,即20秒。 |
poolPingQuery | 发送到数据库的侦测查询,用来检验连接是否处在正常工作秩序中。默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一定的错误消息。 |
poolPingEnabled | 是否启用侦测查询。若开启,必须使用一个可执行的SQL语句设置poolPingQuery属性(最好是一个非常快的SQL),默认值:false。 |
poolPingConnectionsNotUsedFor | 配置poolPingQuery的使用频度。可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认值:0(表示所有连接每一时刻都被侦测,只有poolPingEnabled的属性值为true时适用)。 |
3、JNDI
此数据源可以在EJB或应用服务器等容器中使用。容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。
配置JNDI数据源时,只需要配置两个属性,如表3所示。
表3 JNDI数据源需要配置的属性
属性 | 说明 |
---|---|
initial_context | 此属性主要用于在InitialContext中寻找上下文(即initialContext.lookup(initial_context))。该属性为可选属性,在忽略时,data_source属性会直接从InitialContext中寻找。 |
data_source | 此属性表示引用数据源实例位置的上下文的路径。如果提供了initial_context配置,那么程序会在其返回的上下文中进行查找;如果没有提供,则直接在InitialContext中查找。 |
8.<mappers>元素
在配置文件中,<mappers>元素用于指定MyBatis映射文件的位置,一般可以使用以下4种方法引入映射器文件,具体如下所示。
1. 使用类路径引入
<mappers>
<mapper resource="com/itheima/mapper/UserMapper.xml"/>
</mappers>
2. 使用本地文件路径引入
<mappers>
<mapper url="file:///D:/com/itheima/mapper/UserMapper.xml"/>
</mappers>
3. 使用接口类引入
<mappers>
<mapper class="com.itheima.mapper.UserMapper"/>
</mappers>
4. 使用包名引入
<mappers>
<package name="com.itheima.mapper"/>
</mappers>
上述4种引入方式非常简单,读者可以根据实际项目需要选取使用。
五. 映射文件相当于接口实现类
在映射文件中,<mapper>元素是映射文件的根元素,其他元素都是它的子元素。这些子元素及其作用如图1所示。
图1 映射文件中的主要元素
相当于接口的实现类, namespace指定映射(实现)的接口
<mapper namespace="dao.StudentDAO">
...
</mapper>
1.<select>元素
<select>元素用来映射查询语句,它可以帮助我们从数据库中读取出数据,并组装数据给业务开发人员。
使用<select>元素执行查询操作非常简单,其示例如下:
<select id="findCustomerById" parameterType="Integer"
resultType="com.itheima.po.Customer">
select * from t_customer where id = #{id}
</select>
上述语句中的唯一标识为findCustomerById,它接收一个Integer类型的参数,并返回一个Customer类型的对象。
select元素中,除了上述示例代码中的几个属性外,还有其他一些可以配置的属性,如表1所示。
表1 <select>元素的常用属性
属性 | 说明 |
---|---|
id | 表示命名空间中的唯一标识符,常与命名空间组合起来使用。组合后如果不唯一,MyBatis会抛出异常。 |
parameterType | 该属性表示传入SQL语句的参数类的全限定名或者别名。它是一个可选属性,因为MyBatis可以通过TypeHandler推断出具体传入语句的参数。其默认值是unset(依赖于驱动)。 |
resultType | 从SQL语句中返回的类型的类的全限定名或者别名。如果是集合类型,那么返回的应该是集合可以包含的类型,而不是集合本身。返回时可以使用resultType或resultMap之一。 |
resultMap | 表示外部resultMap的命名引用。返回时可以使用resultType或resultMap之一。 |
flushCache | 表示在调用SQL语句之后,是否需要MyBatis清空之前查询的本地缓存和二级缓存。其值为布尔类型(true|false),默认值为false。如果设置为true,则任何时候只要SQL语句被调用,都会清空本地缓存和二级缓存。 |
useCache | 用来控制二级缓存的开启和关闭。其值为布尔类型(true|false),默认值为true,表示将查询结果存入二级缓存中。 |
timeout | 用于设置超时参数,单位为秒。超时时将抛出异常。 |
fetchSize | 获取记录的总条数设定,其默认值是unset(依赖于驱动)。 |
statementType | 用于设置MyBatis使用哪个JDBC的Statement工作,其值为STATEMENT、PREPARED(默认值)或CALLABLE,分别对应JDBC中的Statement、PreparedStatement和CallableStatement。 |
resultSetType | 表示结果集的类型,其值可设置为FORWARD_ONLY、SCROLL_SENSITIVE或SCROLL_INSENSITIVE,它的默认值是unset(依赖于驱动)。 |
2.<insert>元素
<insert>元素用于映射插入语句,在执行完元素中定义的SQL语句后,会返回一个表示插入记录数的整数。
<insert>元素的配置示例如下:
<insert
id="addCustomer"
parameterType="com.itheima.po.Customer"
flushCache="true"
statementType="PREPARED"
keyProperty=""
keyColumn=""
useGeneratedKeys=""
timeout="20">
从上述示例代码中可以看出,<insert>元素的属性与<select>元素的属性大部分相同,但还包含了3个特有属性,这3个属性的描述如表1所示。
表1 <insert>元素中的属性
属性 | 说明 |
---|---|
keyProperty | (仅对insert和update有用)此属性的作用是将插入或更新操作时的返回值赋值给PO类的某个属性,通常会设置为主键对应的属性。如果需要设置联合主键,可以在多个值之间用逗号隔开。 |
keyColumn | (仅对insert和update有用)此属性用于设置第几列是主键,当主键列不是表中的第一列时需要设置。在需要主键联合时,值可以用逗号隔开。 |
useGeneratedKeys | (仅对insert和update有用)此属性会使MyBatis使用JDBC的getGeneratedKeys()方法来获取由数据库内部生产的主键,如MySQL和SQL Server等自动递增的字段,其默认值为false。 |
执行插入操作后,很多时候我们会需要返回插入成功的数据生成的主键值,此时就可以通过上面所讲解的3个属性来实现。
如果使用的数据库支持主键自动增长(如MySQL),那么可以通过keyProperty属性指定PO类的某个属性是接收主键返回值(通常会设置到id属性上),然后将useGeneratedKeys的属性值设置为true。其使用示例如下:
<insert id="addCustomer" parameterType="com.itheima.po.Customer"
keyProperty="id" useGeneratedKeys="true" >
insert into t_customer(username,jobs,phone)
values(#{username},#{jobs},#{phone})
</insert>
使用上述配置执行插入后,会返回插入成功的行数,以及插入行的主键值。为了验证此配置,可以通过如下代码测试。
@Test
public void addCustomerTest(){
// 获取SqlSession
SqlSession sqlSession = MybatisUtils.getSession();
Customer customer = new Customer();
customer.setUsername("rose");
customer.setJobs("student");
customer.setPhone("13333533092");
int rows = sqlSession.insert("com.itheima.mapper."
+ "CustomerMapper.addCustomer", customer);
// 输出插入数据的主键id值
System.out.println(customer.getId());
if(rows > 0){
System.out.println("您成功插入了"+rows+"条数据!");
}else{
System.out.println("执行插入操作失败!!!");
}
sqlSession.commit();
sqlSession.close();
}
执行程序后,控制台的输出结果如图1所示。
图1 运行结果
如果使用的数据库不支持主键自动增长(如Oracle),或者支持增长的数据库取消了主键自增的规则时,也可以使用MyBatis提供的另一种方式来自定义生成主键,具体配置示例如下:
<insert id="insertCustomer" parameterType="com.itheima.po.Customer">
<selectKey keyProperty="id" resultType="Integer" order="BEFORE">
select if(max(id) is null, 1, max(id) +1) as newId from t_customer
</selectKey>
insert into t_customer(id,username,jobs,phone)
values(#{id},#{username},#{jobs},#{phone})
</insert>
在执行上述示例代码时,<selectKey>元素会首先运行,它会通过自定义的语句来设置数据表中的主键(如果t_customer表中没有记录,则将id设置为1,否则就将id的最大值加1,来作为新的主键),然后再调用插入语句。
<selectKey>元素在使用时可以设置以下几种属性:
<selectKey
keyProperty="id"
resultType="Integer"
order="BEFORE"
statementType="PREPARED">
在上述<selectKey>元素的几个属性中,keyProperty、resultType和statementType的作用与前面讲解的相同,这里不重复介绍。order属性可以被设置为BEFORE或AFTER。如果设置为BEFORE,那么它会首先执行<selectKey>元素中的配置来设置主键,然后执行插入语句;如果设置为AFTER,那么它会先执行插入语句,然后执行<selectKey>元素中的配置内容。
3.<update>元素和<delete>元素
<update>和<delete>元素的使用比较简单,它们的属性配置也基本相同,其常用属性如下所示。
<update
id="updateCustomer"
parameterType="com.itheima.po.Customer"
flushCache="true"
statementType="PREPARED"
timeout="20">
<delete
id="deleteCustomer"
parameterType="com.itheima.po.Customer"
flushCache="true"
statementType="PREPARED"
timeout="20">
从上述配置代码中可以看出,<update>和<delete>元素的属性基本与<select>元素中的属性一致。与<insert>元素一样,<update>和<delete>元素在执行完之后,也会返回一个表示影响记录条数的整数,其使用示例如下:
<!-- 更新信息 -->
<update id="updateCustomer" parameterType="com.itheima.po.Customer">
update t_customer
set username=#{username},jobs=#{jobs},phone=#{phone}
where id=#{id}
</update>
<!-- 删除信息 -->
<delete id="deleteCustomer" parameterType="Integer">
delete from t_customer where id=#{id}
</delete>
4.<sql>元素
<sql>元素的作用就是定义可重用的SQL代码片段,然后在其他语句中引用这一代码片段。例如,定义一个包含id、username、jobs和phone字段的代码片段如下:
<sql id="customerColumns">id,username,jobs,phone</sql>
这一代码片段可以包含在其他语句中使用,具体如下:
<select id="findCustomerById" parameterType="Integer"
resultType="com.itheima.po.Customer">
select <include refid="customerColumns"/>
from t_customer
where id = #{id}
</select>
在上述代码中,使用<include>元素的refid属性引用了自定义的代码片段,refid的属性值为自定义代码片段的id。
上面示例只是一个简单的引用查询。在实际开发中,可以更加灵活的定义SQL片段,其示例如下:
<!--定义表的前缀名 -->
<sql id="tablename">
${prefix}customer
</sql>
<!--定义要查询的表 -->
<sql id="someinclude">
from
<include refid="${include_target}" />
</sql>
<!--定义查询列 -->
<sql id="customerColumns">
id,username,jobs,phone
</sql>
<!--根据id查询客户信息 -->
<select id="findCustomerById" parameterType="Integer"
resultType="com.itheima.po.Customer">
select
<include refid="customerColumns"/>
<include refid="someinclude">
<property name="prefix" value="t_" />
<property name="include_target" value="tablename" />
</include>
where id = #{id}
</select>
上述代码中,定义了3个代码片段,分别为表的前缀名、要查询的表和需要查询的列。前2个代码片段中,分别获取了<include>子元素<property>中的值,其中第1个代码片段中的“${prefix}”会获取name为prefix的值“t_”,获取后所组成的表名为“t_customer”;而第2个代码片段中的“${include_target}”会获取name为include_target的值“tablename”,由于tablename为第1个SQL片段的id值,所以最后要查询的表为“t_customer”。所有的SQL片段在程序运行时,都会由MyBatis组合成SQL语句来执行需要的操作。
执行程序后,控制台的输出结果如图1所示。
图1 运行结果
5.<resultMap>元素
<resultMap>元素表示结果映射集,是MyBatis中最重要也是最强大的元素。它的主要作用是定义映射规则、级联的更新以及定义类型转化器等。
<resultMap>元素中包含了一些子元素,它的元素结构如下所示。
<!--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中未定义无参的构造方法时,就可以使用<constructor>元素进行配置)。子元素<id>用于表示哪个列是主键,而<result>用于表示POJO和数据表中普通列的映射关系。<association>和<collection>用于处理多表时的关联关系,而<discriminator>元素主要用于处理一个单独的数据库查询返回很多不同数据类型结果集的情况。
在默认情况下,MyBatis程序在运行时会自动的将查询到的数据与需要返回的对象的属性进行匹配赋值(需要表中的列名与对象的属性名称完全一致)。然而实际开发时,数据表中的列和需要返回的对象的属性可能不会完全一致,这种情况下MyBatis是不会自动赋值的。此时,就可以使用<resultMap>元素进行处理。
接下来,通过一个具体的案例来演示<resultMap>元素在此种情况的使用,具体步骤如下。
(1)在mybatis数据库中,创建一个t_user表,并插入几条测试数据。
USE mybatis;
CREATE TABLE t_user(
t_id INT PRIMARY KEY AUTO_INCREMENT,
t_name VARCHAR(50),
t_age INT
);
INSERT INTO t_user(t_name,t_age) VALUES('Lucy',25);
INSERT INTO t_user(t_name,t_age) VALUES('Lili',20);
INSERT INTO t_user(t_name,t_age) VALUES('Jim',20);
(2)在com.itheima.po包中,创建持久化类User,并在类中定义id、name和age属性,以及其getter/setter方法和toString()方法,如文件1所示。
文件1 User.java
1 package com.itheima.po;
2 public class User {
3 private Integer id;
4 private String name;
5 private Integer age;
6 public Integer getId() {
7 return id;
8 }
9 public void setId(Integer id) {
10 this.id = id;
11 }
12 public String getName() {
13 return name;
14 }
15 public void setName(String name) {
16 this.name = name;
17 }
18 public Integer getAge() {
19 return age;
20 }
21 public void setAge(Integer age) {
22 this.age = age;
23 }
24 @Override
25 public String toString() {
26 return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
27 }
28 }
(3)在com.itheima.mapper包下,创建映射文件UserMapper.xml,并在映射文件中编写映射查询语句,如文件2所示。
文件2 UserMapper.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE mapper
3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5 <mapper namespace="com.itheima.mapper.UserMapper">
6 <resultMap type="com.itheima.po.User" id="resultMap">
7 <id property="id" column="t_id"/>
8 <result property="name" column="t_name"/>
9 <result property="age" column="t_age"/>
10 </resultMap>
11 <select id="findAllUser" resultMap="resultMap">
12 select * from t_user
13 </select>
14 </mapper>
在文件2中,<resultMap>的子元素<id>和<result>的property属性表示User类的属性名,column属性表示数据表t_user的列名。<select>元素的resultMap属性表示引用上面定义的resultMap。
(4)在配置文件mybatis-config.xml中,引入UserMapper.xml。
(5)在测试类中,编写测试方法findAllUserTest(),代码如下所示。
@Test
public void findAllUserTest() {
// 获取SqlSession
SqlSession sqlSession = MybatisUtils.getSession();
// SqlSession执行映射文件中定义的SQL,并返回映射结果
List<User> list =
sqlSession.selectList("com.itheima.mapper.UserMapper.findAllUser");
for (User user : list) {
System.out.println(user);
}
// 关闭SqlSession
sqlSession.close();
}
使用JUnit4执行上述方法后,控制台的输出结果如图1所示。
图1 运行结果
从图1可以看出,虽然t_user表的列名与User对象的属性名完全不一样,但查询出的数据还是被正确的封装到了User对象中。