Mapper XML文件
MyBatis的真正威力在Mapped Statements中。这是魔术发生的地方。对于他们的所有权力,Mapper XML文件相对简单。当然,如果您要将它们与等效的JDBC代码进行比较,您会立即看到95%的代码节省。MyBatis的建立是为了专注于SQL,并竭尽全力保持自己的方式。
Mapper XML文件只有一些第一类元素(按照它们应该定义的顺序):
- 缓存 - 配置给定名称空间的缓存。
- cache-ref - 引用另一个命名空间的缓存配置。
- resultMap - 描述如何从数据库结果集加载对象的最复杂和最强大的元素。
参数映射 - 弃用!老派的方式来映射参数。内联参数是首选,这个元素将来可能会被删除。这里没有记录。- sql - 可以被其他语句引用的可重用的SQL块。
- 插入 - 映射的INSERT语句。
- 更新 - 映射的UPDATE语句。
- 删除 - 映射的DELETE语句。
- 选择 - 一个映射的SELECT语句。
接下来的部分将详细描述这些元素,从语句本身开始。
选择
select语句是MyBatis中最受欢迎的元素之一。把数据放到数据库中是不可贵的,除非你把它们还原出来,所以大多数应用程序的查询要比修改数据要多得多。对于每一个插入,更新或删除,可能有很多选择。这是MyBatis的创始原则之一,也是查询和结果映射的焦点和努力的原因。select元素对于简单的情况非常简单。例如:
<select id="selectPerson" parameterType="int" resultType="hashmap"> SELECT * FROM PERSON WHERE ID = #{id} </select>
这个语句被称为selectPerson,它接受一个int(或Integer)类型的参数,并返回一个由列名映射到行值的HashMap。
注意参数符号:
#{id}
这告诉MyBatis创建一个PreparedStatement参数。使用JDBC,这样的参数将被标识为“?” 在SQL中传递给一个新的PreparedStatement,如下所示:
// Similar JDBC code, NOT MyBatis… String selectPerson = "SELECT * FROM PERSON WHERE ID=?"; PreparedStatement ps = conn.prepareStatement(selectPerson); ps.setInt(1,id);
当然,JDBC需要更多的代码来提取结果并将它们映射到一个对象的实例,这是MyBatis不需要做的事情。关于参数和结果映射的知识还有很多。这些细节保证他们自己的部分,这在本节后面。
select元素具有更多的属性,允许您配置每个语句应该如何表现的细节。
<select id="selectPerson" parameterType="int" parameterMap="deprecated" resultType="hashmap" resultMap="personResultMap" flushCache="false" useCache="true" timeout="10000" fetchSize="256" statementType="PREPARED" resultSetType="FORWARD_ONLY">
属性 | 描述 |
---|---|
id | 此名称空间中的唯一标识符,可用于引用此语句。 |
parameterType | 将传递给此语句的参数的完全限定类名称或别名。这个属性是可选的,因为MyBatis可以计算TypeHandler来使用传递给语句的实际参数。默认值未设置。 |
| |
resultType | 将从此语句返回的预期类型的完全限定类名或别名。请注意,对于集合,这应该是集合包含的类型,而不是集合本身的类型。使用resultType或resultMap,不能同时使用。 |
resultMap | 对外部resultMap的命名引用。结果图是MyBatis最强大的功能,通过对它们的了解,可以解决很多困难的映射案例。使用resultMap或resultType,而不是两者。 |
flushCache | 如果将此语句设置为true,则会在调用此语句时刷新本地和第二级高速缓存。默认值:false选择语句。 |
useCache | 将其设置为true将导致此语句的结果缓存在二级缓存中。默认值: 对于select语句是true。 |
timeout | 这会设置驱动程序在抛出异常之前等待数据库返回请求的秒数。默认值是未设置的(取决于驱动程序)。 |
fetchSize | 这是一个驱动程序提示,它将尝试使驱动程序返回大小等于此设置的成批行编号的结果。默认值是未设置的(取决于驱动程序)。 |
statementType | 任何一个STATEMENT,PREPARED或CALLABLE。这导致MyBatis分别使用Statement, PreparedStatement或CallableStatement。默认值:PREPARED。 |
resultSetType | 任何一个FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE。默认值是未设置的(取决于驱动程序)。 |
databaseId | 如果存在已配置的databaseIdProvider,则MyBatis将加载所有没有databaseId 属性的语句或与当前匹配的databaseId。如果遇到相同的语句,如果发现有和没有databaseId后者将被丢弃。 |
resultOrdered | 这只适用于嵌套的结果选择语句:如果这是真的,则假定嵌套结果被包含或组合在一起,使得当返回新的主结果行时,不再引用先前的结果行。这使得嵌套结果可以被更多的内存填充。默认: false。 |
resultSets | 这只适用于多个结果集。它列出了将由语句返回的结果集并给出每个结果集的名称。名称用逗号分隔。 |
插入,更新和删除
<insert id="insertAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" keyProperty="" keyColumn="" useGeneratedKeys="" timeout="20"> <update id="updateAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20"> <delete id="deleteAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20">
数据修改语句插入,更新和删除在它们的实现中非常相似:
属性 | 描述 |
---|---|
id | 此名称空间中的唯一标识符,可用于引用此语句。 |
parameterType | 将传递给此语句的参数的完全限定类名称或别名。这个属性是可选的,因为MyBatis可以计算TypeHandler来使用传递给语句的实际参数。默认值未设置。 |
flushCache | 如果将此语句设置为true,则会在调用此语句时导致第二级和本地缓存刷新。缺省:对于插入,更新和删除语句为true。 |
timeout | 这会在抛出异常之前设置驱动程序等待数据库从请求返回的最大秒数。默认值是未设置的(取决于驱动程序)。 |
statementType | 任何一个STATEMENT,PREPARED或CALLABLE。这导致MyBatis分别使用Statement, PreparedStatement或CallableStatement。默认值:PREPARED。 |
useGeneratedKeys | (仅插入和更新)这告诉MyBatis使用JDBC getGeneratedKeys方法来检索数据库内部生成的密钥(例如在RDBMS中自动增加字段,如MySQL或SQL Server)。默认:false |
keyProperty | (仅插入和更新)标识MyBatis将设置由getGeneratedKeys或insert语句的selectKey子元素返回的键值的属性。默认:未设置。如果需要多个生成的列,可以是逗号分隔的属性名称列表。 |
keyColumn | (仅插入和更新)使用生成的密钥设置表中列的名称。这只在某些数据库(如PostgreSQL)中当键列不是表中的第一列时才是必需的。如果需要多个生成的列,可以是逗号分隔的列名称列表。 |
databaseId | 如果存在已配置的databaseIdProvider,则MyBatis将加载所有没有databaseId 属性的语句或与当前匹配的databaseId。如果遇到相同的语句,如果发现有和没有databaseId后者将被丢弃。 |
以下是插入,更新和删除语句的一些示例。
<insert id="insertAuthor"> insert into Author (id,username,password,email,bio) values (#{id},#{username},#{password},#{email},#{bio}) </insert> <update id="updateAuthor"> update Author set username = #{username}, password = #{password}, email = #{email}, bio = #{bio} where id = #{id} </update> <delete id="deleteAuthor"> delete from Author where id = #{id} </delete>
如前所述,插入是一个更丰富的一点,它有一些额外的属性和子元素,使其能够以多种方式处理密钥生成。
首先,如果您的数据库支持自动生成的关键字段(例如MySQL和SQL Server),那么您可以简单地设置useGeneratedKeys =“true”并将keyProperty设置为目标属性,即可完成。例如,如果上面的Author表为ID使用了一个自动生成的列类型,那么语句将被修改如下:
<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id"> insert into Author (username,password,email,bio) values (#{username},#{password},#{email},#{bio}) </insert>
如果您的数据库也支持多行插入,则可以传递一个或多个Author的数组并检索自动生成的密钥。
<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id"> insert into Author (username, password, email, bio) values <foreach item="item" collection="list" separator=","> (#{item.username}, #{item.password}, #{item.email}, #{item.bio}) </foreach> </insert>
MyBatis有另一种方法来处理不支持自动生成列类型的数据库的密钥生成,或者还不支持JDBC驱动程序对自动生成密钥的支持。
这是一个简单的(愚蠢的)例子,它会产生一个随机的ID(你可能永远不会这么做,但是这表明了灵活性以及MyBatis真的不介意):
<insert id="insertAuthor"> <selectKey keyProperty="id" resultType="int" order="BEFORE"> select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1 </selectKey> insert into Author (id, username, password, email,bio, favourite_section) values (#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR}) </insert>
在上面的例子中,selectKey语句会先运行,Author id属性被设置,然后insert语句被调用。这给你一个类似于你的数据库中自动生成的键的行为,而不会使你的Java代码复杂化。
selectKey元素描述如下:
<selectKey keyProperty="id" resultType="int" order="BEFORE" statementType="PREPARED">
属性 | 描述 |
---|---|
keyProperty | selectKey语句的结果应该被设置的目标属性。如果需要多个生成的列,可以是逗号分隔的属性名称列表。 |
keyColumn | 返回结果集中与该属性匹配的列名称。如果需要多个生成的列,可以是逗号分隔的列名称列表。 |
resultType | 结果的类型。MyBatis通常可以解决这个问题,但添加它确实没有什么坏处。MyBatis允许使用任何简单的类型作为键,包括字符串。如果您希望生成多个列,那么可以使用包含期望属性的对象或Map。 |
order | 这可以设置为BEFORE或AFTER。如果设置为BEFORE,那么将首先选择键,设置keyProperty然后执行插入语句。如果设置为AFTER,它将运行insert语句,然后运行selectKey语句 - 对于像Oracle这样的可能在嵌入语句中嵌入序列调用的数据库是很常见的。 |
statementType | 和上面一样,MyBatis支持分别映射到Statement,PreparedStatement和CallableStatement的STATEMENT,PREPARED和CALLABLE语句类型。 |
SQL
这个元素可以用来定义可以包含在其他语句中的可重用的SQL代码片段。它可以静态地(在加载阶段)参数化。包含实例中的不同属性值可能有所不同。例如:
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
然后可以将SQL片段包含在另一个语句中,例如:
<select id="selectUsers" resultType="map"> select <include refid="userColumns"><property name="alias" value="t1"/></include>, <include refid="userColumns"><property name="alias" value="t2"/></include> from some_table t1 cross join some_table t2 </select>
属性值也可以用在include子句中,包括refid属性或属性值,例如:
<sql id="sometable"> ${prefix}Table </sql> <sql id="someinclude"> from <include refid="${include_target}"/> </sql> <select id="select" resultType="map"> select field1, field2, field3 <include refid="someinclude"> <property name="prefix" value="Some"/> <property name="include_target" value="sometable"/> </include> </select>
Parameters
在所有的过去的陈述中,你已经看到了简单参数的例子。参数是MyBatis中非常强大的元素。对于简单的情况,大概有90%的情况,对他们来说并不多,例如:
上面的例子演示了一个非常简单的命名参数映射。参数类型被设置为 int,因此参数可以被命名为任何东西。原始数据类型或简单数据类型(如 Integer和String)没有相关属性,因此将完全替换参数的完整值。但是,如果你传入一个复杂的对象,那么行为就有点不同了。例如:
<insert id="insertUser" parameterType="User"> insert into users (id, username, password) values (#{id}, #{username}, #{password}) </insert>
如果User类型的参数对象被传递到该语句中,则会查找id,username和password属性,并将其值传递给PreparedStatement参数。
将参数传递给语句非常简单。但是参数图有很多其他的特点。
首先,像MyBatis的其他部分一样,参数可以指定更具体的数据类型。
#{property,javaType=int,jdbcType=NUMERIC}
像MyBatis的其余部分一样,javaType几乎总是可以从参数对象中确定,除非该对象是一个HashMap。然后java类型应规定以确保正确的 类型处理器使用。
注:如果null作为值传递,则JDBC类型对于所有可为空的列是JDBC所必需的。您可以通过阅读PreparedStatement.setNull()方法的JavaDocs来调查。
要进一步自定义类型处理,还可以指定一个特定的TypeHandler 类(或别名),例如:
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
所以已经看起来很冗长了,但事实是,你很少会设置其中的任何一个。
对于数字类型,还有一个numericScale用于确定相关的小数位数。
#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}
最后,mode属性允许你指定IN,OUT或INOUT参数。如果参数是 OUT或INOUT,那么参数对象属性的实际值将会改变,就像您在调用输出参数时所预期的一样。如果mode = OUT(或INOUT)和 jdbcType = CURSOR(即Oracle REFCURSOR),则必须指定resultMap以将ResultSet 映射到参数的类型。请注意,javaType属性在这里是可选的,它将被自动设置为ResultSet如果留空,CURSOR作为jdbcType。
#{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentResultMap}
MyBatis也支持更高级的数据类型,比如结构体,但是在注册out参数的时候,你必须告诉这个语句类型的名字。例如(再次,在实践中不要像这样断线):
#{middleInitial, mode=OUT, jdbcType=STRUCT, jdbcTypeName=MY_TYPE, resultMap=departmentResultMap}
尽管所有这些强大的选项,大部分时间你只需指定属性名称,MyBatis将找出其余的。至多,你会指定可空列的jdbcType。
#{firstName} #{middleInitial,jdbcType=VARCHAR} #{lastName}