二 mybatis配置文件详解
MyBatis最关键的组成部分是SqlSessionFactory,我们可以从中获取 SqlSession,并执行映射的SQL语句。Sql Session Factory对象可以通过基于XML的配置信息或者JavaAPI创建。
2.1 使用xml配置Mybatis
构建SqlSessionFactory最常见的方式是基于XML配置。下面的 mybatis-config.xml展示了一个典型的MyBatis配置文件的样子:
<?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>
<properties resource="application.properties">
<property name="username" value="db_user" />
<property name="password" value="verysecurepwd" />
</properties>
<settings>
<setting name="cacheEnabled" value="true" />
</settings>
<typeAliases>
<typeAlias alias="Student" type="com.briup.pojo.Student" />
<package name="com.briup.pojo" />
</typeAliases>
<typeHandlers>
<typeHandler handler="com.mybatis3.typehandlers.PhoneTypeHandler" />
<package name="com.briup.typehandlers" />
</typeHandlers>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
<environment id="production">
<transactionManager type="MANAGED" />
<dataSource type="JNDI">
<property name="data_source" value="java:comp/env/jdbc/MyBatisDemoDS" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/briup/mappers/StudentMapper.xml" />
<mapper url="file:///D:/mybatisdemo/mappers/StudentMapper.xml" />
<mapper class="com.briup.mappers.StudentMapper" />
</mappers>
</configuration>
一个完整的config xml文档就包含了:
1.configuration
propertis
settings
typeAliases
typeHandlers
objectFactory
plugins
environments
environment
transactionManager
dataSource
databaseIdProvider
mappers
2.1.1 environments是配置mybatis当前工作的数据库环境的地方
MyBatis支持配置多个dataSource环境,可以将应用部署到不同的环境上,如DEV(开发环境),TEST(测试换将),QA(质量评估环境),UAT(用户验收环境),PRODUCTION(生产环境),可以通过将默认environments值设置成想要的environment的id值。
有时候,我们可能需要在相同的应用下使用多个数据库,比如我们可能有SHOPPING-CART数据库来存储所有的订单明细;使用REPORTS数据库存储订单明细的合计,用作报告。(也就是如果系统在运行期间如果有切换数据库环境的需求,mybatis中也可以很轻松的实现).
如果你的应用需要连接多个数据库,你需要将每个数据库配置成独立的环境,并且为每一个数据库创建一个SqlSessionFactory
例如:
<environments default="shoppingcart">
<environment id="shoppingcart">
<transactionManager type="MANAGED" />
<dataSource type="JNDI">
<property name="data_source" value="java:comp/env/jdbc/ShoppingcartDS" />
</dataSource>
</environment>
<environment id="reports">
<transaction Managertype="MANAGED" />
<dataSource type="JNDI">
<property name="data_source" value="java:comp/env/jdbc/ReportsDS" />
</dataSource>
</environment>
</environments>
我们可以如下为以上每个环境创建一个SqlSessionFactory
java代码:
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//默认的环境
defaultSqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//统计明细的环境
cartSqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, "shoppingcart");
//统计报表的环境
reportSqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, "reports");
注意:对于environments,我们可以在其中配置多个environment子元素,同时还需要在environment中配置dataSource和transactionManager元素。
2.1.2 dataSource元素
dataSource表示的是数据源:至少会包括该连接数据库的各种信息
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
dataSource的类型type属性可以配置成其内置类型之一,如UNPOOLED,POOLED,JNDI。
1.UNPOOLED:如果将类型设置成UNPOOLED,MyBatis会为每一个数据库操作创建一个新的连接,并关闭它。该方式适用于只有小规模数量并发用户的简单应用程序上。
2.POOLED:如果将属性设置成POOLED,MyBatis会创建一个数据库连接池,连接池中的一个连接将会被用作数据库操作。一旦数据库操作完成,MyBatis会将此连接返回给连接池。在开发或测试环境中,经常使用此种方式。
3.JNDI:如果将类型设置成JNDI(Java Naming and Directory Interface,Java命名和目录接口,是SUN公司提供的一种标准的Java命名系统接口),MyBatis从应用服务器上配置好的JNDI数据源dataSource获取数据库连接。在生产环境中,优先考虑这种方式。
4.自定义:可以通过实现接口org.apache.ibatis.datasource.DataSourceFactory来使用第三方数据源实现:
例如:我们使用阿里提供的DruidDataSource,
public class MyDuridDataSource implements DataSourceFactory {
private Properties props ;
@Override
public DataSource getDataSource() {
try {
return DruidDataSourceFactory.createDataSource(props);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
@Override
public void setProperties(Properties arg0) {
this.props = arg0;
}
}
配置:
<dataSource type="com.briup.common.MyDuridDataSource" >
<property name="driver" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
<property name="username" value="briup" />
<property name="password" value="briup" />
</dataSource>
2.1.3 transactionManager元素 :事务管理器
MyBatis支持两种类型的事务管理器:JDBC 和 MANAGED.
JDBC: 当应用程序负责管理数据库连接的生命周期(提交、回退等等)的时候使用。当你将TransactionManager 属性设置成JDBC,MyBatis内部将使用JdbcTransactionFactory类创建TransactionManager。例如,部署到ApacheTomcat的应用程序,需要应用程序自己管理事务。
MANAGED:当由应用服务器负责管理数据库连接生命周期的时候使用。当你将TransactionManager属性设置成MANAGED时,MyBatis内部使用ManagedTransactionFactory类创建事务管理器TransactionManager。例如,当一个Java EE的应用程序部署在类似JBoss,WebLogic,GlassFish应用服务器上时,它们会使用EJB进行应用服务器的事务管理能力。在这些管理环境中,你可以使用MANAGED事务管理器。
注:Managed 是托管的意思,即是应用本身不去管理事务,而是把事务管理交给应用所在的服务器进行管理。
2.1.4 properties
可以用来加载java外部properties文件,或者通过property子标签传递数据。
例如加载外部文件:db.properties(放在classpath下)
<properties resource="db.properties">
<property name="username" value="zhangsan">
</properties>
这些属性将会在贯穿整个配置文件,在一些动态配置的地方使用${propName}的方式取值。
<dataSource type="POOLED">
<property name="driver" value="${driver}">
<property name="username" value="${username}">
</dataSource>
在DataSource配置中的property中${driver}会使用db.properties文件中声明的driver替换,而${username}将会使用上方property标签中配置的username替换。
Properties对象也能作为参数传递给SqlSessionBuilder.builder()方法。例如:
SqlSessionBuilder.builder(InputStream,Properties);
note:
如果一个属性出现在多个地方,MyBatis加载的顺序为:
1.在properties元素中的属性会首先读取。
<property>
2.在Properties标签的reousrce和url两个属性上配置的资源文件第二个读取,并且会覆盖已经存在的属性
3.作为方法的参数传递过来的属性将最后读取,并且覆盖原来通过property标签和Properties的resource/url属性加载的元素。
优先级别: builder方法参数>url/resource属性>property标签。
2.1.5 mappers元素: SQL映射定义
SQL Mapper文件中包含的SQL映射语句将会被应用通过使用其标签中的id值来执行。我们需要在mybatis-config.xml文件中配置SQL Mapper文件的位置。
<mappers>
<mapper resource="com/briup/mappers/StudentMapper.xml" />
<mapper url="file:///D:/mybatisdemo/app/mappers/StudentMapper.xml" />
<mapper class="com.briup.mappers.StudentMapper" />
<package name="com.briup.mappers" />
</mappers>
以上每一个<mapper> 标签的属性有助于从不同类型的资源中加载映射mapper:
resource属性用来指定在classpath中的mapper文件。
url属性用来通过完全文件系统路径或者web URL地址来指向mapper文件
class属性用来指向一个mapper接口
package属性用来指向可以找到Mapper接口的包名
2.1.6 typeAliases元素: 类型别名
在SQLMapper配置文件中,对于resultType和parameterType属性值,我们需要使用JavaBean 的完全限定名。
例如:
<select id="findById" parameterType="java.lang.Long" resultType="com.briup.bean.Student">
select * from test where id = #{id}
</select>
<update id="updateUser" parameterType="com.briup.bean.Student">
update test set name = #{name},gender=#{gender},birthday=#{birthday}
where id = #{id}
</update>
注:parameterType表示将来调用这个sql语句的时候所传的参数的类型(参数值用来替换sql语句中的占位符)
resultType表示将来调用这个sql语句的时候所返回的结果的类型(方便mybatis给我们自动封装结果集)
这里我们为resultType和parameterType属性值设置为Student类型的完全限定名:com.briup.bean.Student
我们可以为完全限定名取一个别名(alias),然后其需要使用完全限定名的地方使用别名,而不是到处使用完全限定名。如下例子所示,为完全限定名起一个别名:
<type Aliases>
<type Alias alias="Student" type="com.briup.bean.Student" />
<package name="com.bean.pojo" />
</type Aliases>
然后在SQLMapper映射文件中, 如下使用Student的别名:
<select id="findById" parameterType="java.lang.Long" resultType="Student">
select * from test where id = #{id}
</select>
<update id="updateUser" parameterType="Student">
update test set name = #{name},gender=#{gender},birthday=#{birthday}
where id = #{id}
</update>
你可以不用为每一个JavaBean单独定义别名,你可以为提供需要取别名的JavaBean所在的包(package),MyBatis会自动扫描包内定义的JavaBeans,然后分别为JavaBean注册一个小写字母开头的非完全限定的类名形式的别名。如下所示,提供一个需要为JavaBeans起别名的包名:
<type Aliases>
<package name="com.briup.pojo" />
</type Aliases>
如果Student.java和 Teacher.java Bean定义在com.briup.pojo包中,则 com.briup.pojo.Student的别名会被注册为student。而com.briup.pojo.Teacher别名将会被注册为teacher
三、Mapper File
关系型数据库和SQL是经受时间考验和验证的数据存储机制。和其他的ORM 框架如Hibernate不同,My Batis鼓励开发者可以直接使用数据库,而不是将其对开发者隐藏,因为这样可以充分发挥数据库服务器所提供的SQL语句的巨大威力。
与此同时,MyBaits消除了书写大量冗余代码的痛苦,它让使用SQL更容易。在代码里直接嵌套SQL语句是很差的编码实践,并且维护起来困难。MyBaits使用了映射器配置文件或注解来配置SQL语句。
3.1 映射器配置文件和映射器接口
我们已经看见了一些在映射器配置文件中配置基本的映射语句,以及怎样使用SqlSession对象调用它们的例子。现在让我们看一下在com.briup.mappers包中的StudentMapper.xml 配置文件内,是如何配置id为”findStudentById”的SQL语句的,代码如下:
<?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.briup.mappers.StudentMapper">
<select id="findStudentById" parameterType="int" result Type="Student">
select id,name,gender,birthday from test where id = #{id}
</select>
</mapper>
我们可以通过下列代码调用findStudentById映射的SQL语句:
public Student findStudentById(Integer studId) {
SqlSession sqlSession = MyBatisSqlSessionFactory.openSession();
try
{
Student student =
sqlSession.selectOne("com.briup.mappers.StudentMapper.
findStudentById", studId);
return student;
}
finally {
sql Session.close();
}
}
我们可以通过字符串(字符串形式为:映射器配置文件所在的包名的namespace + 在文件内定义的语句id,如上,即包名com.briup.mappers.StudentMapper和语句id的值findStudentById组成)调用映射的SQL语句,但是这种方式容易出错。你需要检查映射器配置文件中的定义,以保证你的输入参数类型和结果返回类型是有效的。
【重点部分:】
My Batis通过使用映射器Mapper接口提供了更好的调用映射语句的方法。一旦我们通过映射器配置文件配置了映射语句,我们可以创建一个完全对应的一个映射器接口,接口名跟配置文件名相同,接口所在包名也跟配置文件所在包名完全一样(如StudentMapper.xml所 在 的包名是 com.briup.mappers ,对应的接口名就是com.briup.mappers.StudentMapper.java)。映射器接口中的方法签名也跟映射器配置文件中完全对应:方法名为配置文件中id值;方法参数类型为parameterType对应值;方法返回值类型为returnType对应值。
上述的StudentMapper.xml文件,我们可以创建一个映射器接口StudentMapper.java如下:
package com.briup.mappers;
public interface StudentMapper{
Student findStudentById(Integer id);
}
在Student Mapper.xml映射器配置文件中,其名空间namespace应该跟StudentMapper接口的完全限定名保持一致。另外,Student Mapper.xml中语句id, parameterType,returnType 应该分别和StudentMapper接口中的方法名,参数类型,返回值相对应。
使用映射器接口我们可以以类型安全的形式调用调用映射语句。如下所示:
public Student findStudentById(Integer studId){
SqlSession sqlSession = MyBatisSqlSessionFactory.openSession();
try {
StudentMapper studentMapper =
sqlSession.getMapper(StudentMapper.class);
return student Mapper.findStudentById(studId);
}
finally {
sqlSession.close();
}
}
3.2 映射语句
MyBatis提供了多种元素来配置不同类型的语句,如SELECT,INSERT,UPDATE,DELETE。让我们看看如何具体配置映射语句
3.2.1 INSERT 插入语句
一个INSERTSQL语句可以在<insert>元素在映射器XML配置文件中配置,如下所示:
<insert id="insertStudent" parameterType="Student">
INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL, PHONE)
VALUES(#{stud Id},#{name},#{email},#{phone})
</insert>
这里我们设置一个ID属性为insertStudent,可以在名空间 com.briup.mappers.StudentMapper.insertStudent中唯一标识该sql语句。parameterType 属性是一个完全限定类名或者是一个类型别名(alias)。
我们可以如下调用这个语句:
int count = sqlSession.insert("com.briup.mappers.StudentMapper.insertStudent", student);
sqlSession.insert() 方法返回执行 INSERT 语句后所影响的行数。
如果不使用名空间(namespace)和语句 id 来调用映射语句,你可以通过创建一个映射器Mapper 接口,并以类型安全的方式调用方法,如下所示:
package com.briup.mappers;
public interface Student Mapper{
int insert Student(Student student);
}
你可以如下调用insertStudent映射语句:
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
int count = mapper.insertStudent(student);
自动生成主键:
在上述的INSERT语句中,我们为可以自动生成(auto-generated)主键的列 STUD_ID 插入值。我们可以使用useGeneratedKeys和keyProperty属性让数据库生成auto_increment列的值,并将生成的值设置到其中一个输入对象属性内,如下所示:
<insert id="insertStudent" parameterType="Student" useGeneratedKeys="true"
keyProperty="studId">
INSERT INTO STUDENTS(NAME, EMAIL, PHONE)
VALUES(#{name},#{email},#{phone})
</insert>
这里STUD_ID列值将会被MySQL数据库自动生成,并且生成的值会被设置到student对象的 studId属性上。
有些数据库如Oracle并不支持AUTO_INCREMENT列,其使用序列(SEQUENCE)来生成主键值。假设我们有一个名为STUD_ID_SEQ的序列来生成SUTD_ID主键值。使用如下代码来生成主键:
<insert id="insertStudent" parameterType="Student">
<selectKey keyProperty="studId" resultType="int" order="BEFORE">
SELECT STUD_ID_SEQ.NEXTVAL FROM DUAL
</selectKey>
INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL, PHONE)
VALUES(#{studId},#{name},#{email},#{phone})
</insert>
这里我们使用了<selectKey>子元素来生成主键值,并将值保存到Student对象的studId 属性上。属性order=“before”表示MyBatis将取得序列的下一个值作为主键值,并且在执行INSERT SQL语句之前将值设置到studId属性上。
我们也可以插入时直接获取序列的下一个值,对应的INSERT映射语句如下所示:
<insert id="insertStudent" parameter Type="Student">
INSERT INTO STUDENTS(NAME,EMAIL, PHONE)
VALUES(STUD_ID_SEQ.nextval,#{name},#{email},#{phone})
<selectKey keyProperty="studId" resultType="int" order="AFTER">
SELECT STUD_ID_SEQ.CURRVAL FROM DUAL
</selectKey>
</insert>
3.2.2 UPDATE 更新语句
一个UPDATE SQL语句可以在<update>元素在映射器XML配置文件中配置,如下所示:
<update id="updateStudent" parameterType="Student">
UPDATE STUDENTS SET NAME=#{name}, EMAIL=#{email}, PHONE=#{phone}
WHERE STUD_ID=#{studId}
</update>
我们可以如下调用此语句(iBatis):
int noOfRows Updated = sqlSession.update("com.briup.mappers.StudentMapper.updateStudent", student);
sqlSession.update()方法返回执行UPDATE语句之后影响的行数。
如果不使用名空间(namespace)和语句id来调用映射语句,你可以通过创建一个映射器Mapper接口,并以类型安全的方式调用方法,如下所示(myBatis 推荐使用):
package com.briup.mappers;
public interface StudentMapper{
int updateStudent(Student student);
}
你可以使用映射器Mapper接口来调用updateStudent语句,如下所示:
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
int noOfRowsUpdated = mapper.updateStudent(student);
3.2.3 DELETE 删除语句
一个UPDATE SQL语句可以在<update>元素在映射器XML配置文件中配置,如下所示
<delete id="deleteStudent" parameterType="int">
DELETE FROM STUDENTS WHERE STUD_ID=#{stud Id}
</delete>
我们可以如下调用此语句:
int studId = 1;
int noOfRowsDeleted = sqlSession.delete("com.briup.mappers.StudentMapper.delete Student", studId);
sqlSession.delete()方法返回 delete 语句执行后影响的行数。
如果不使用名空间(namespace)和语句 id 来调用映射语句,你可以通过创建一个映射器 Mapper 接口,并以类型安全的方式调用方法,如下所示:
package com.briup.mappers;
public interface StudentMapper{
int deleteStudent(int studId);
}
你可以使用映射器Mapper接口来调用updateStudent语句,如下所示:
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
int noOfRowsDeleted = mapper.deleteStudent(studId);
3.2.4 SELECT 查询语句
MyBatis真正强大的功能,在于映射SELECT查询结果到JavaBeans方面的极大灵活性。
让我们看看一个简单的select查询是如何(在MyBatis中)配置的,如下所示:
<select id="findStudentById" parameterType="int"
resultType="Student">
SELECT STUD_ID, NAME, EMAIL, PHONE
FROM STUDENTS
WHERE STUD_ID=#{stud Id}
</select>
我们可以如下调用此语句:
int studId = 1;
Student student = sqlSession.selectOne("com.briup.mappers. StudentMapper.findStudentById", studId);
如果不使用名空间(namespace)和语句 id 来调用映射语句,你可以通过创建一个映射器 Mapper 接口,并以类型安全的方式调用方法,如下所示:
package com.briup.mappers;
public interface StudentMapper{
Student findStudentById(Integer studId);
}
你可以使用映射器Mapper接口来调用 findStudentById 语句,如下所示:
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.findStudentById(studId);
如果你检查Student对象的属性值,你会发现studId属性值并没有被stud_id列值填充。这是因为MyBatis自动对JavaBean中和列名匹配的属性进行填充。这就是为什么name,email和 phone属性被填充而studId属性没有被填充。
解决这一问题,我们可以为列名起一个可以与JavaBean中属性名匹配的别名,如下所示:
<select id="findStudentById" parameterType="int"
resultType="Student">
SELECT STUD_ID AS studId, NAME,EMAIL, PHONE
FROM STUDENTS
WHERE STUD_ID=#{studId}
</select>
MyBatis执行返回多条结果的SELECT语句查询,如下所示:
<select id="findAllStudents" resultType="Student">
SELECT STUD_ID AS studId, NAME,EMAIL, PHONE
FROM STUDENTS
</select>
List<Student> students =
sqlSession.selectList("com.briup.mappers.StudentMapper.findAllStudents");
映射器 Mapper 接口 StudentMapper 可以如下定义:
package com.briup.mappers;
public interface StudentMapper{
List<Student> findAllStudents();
}
使用上述代码,我们可以如下调用
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.findAllStudents();
如果你注意到上述的SELECT映射定义,你可以看到,我们为所有的映射语句中的stud_id 起了别名。我们可以使用ResultMaps,来避免上述的到处重复别名。我们稍后会继续讨论。
除了java.util.List,你也可以是由其他类型的集合类,如Set,Map,以及(Sorted Set)。MyBatis 根据集合的类型,会采用适当的集合实现,如下所示:
对于List,Collection,Iterable类型,MyBatis将返回java.util.ArrayList
对于Map类型,MyBatis 将返回java.util.HashMap
对于Set类型,MyBatis 将返回java.util.HashSet
对于SortedSet类型,MyBatis将返回java.util.TreeSet
例如:
<select id="findByName" resultType="map" parameterType="java.lang.String">
select id,name,age,birthday from s_user where name = #{name}
</select>
返回的是map类型的resultType,此时map集合就相当于我们原来的user对象,当前一个map集合中存放了检索出来的id,name,age,birthday等信息的值。
Map<String, Object> mm = session.selectOne(
"com.briup.mappers.UserMapper.findByName", "lisi4");
如果当前select语句不止返回一个结果,那么该select方法执行完成之后,返回类型应该为List<Map<String,Obejct>>
List<Map<String, Object>> lists = session.selectList(
"com.briup.mappers.UserMapper.findByName", "lisi4");
3.3 结果集映射 ResultMaps
ResultMaps被用来将SELECT语句的结果集映射到JavaBeans的属性中。我们可以定义结果集映射ResultMaps并且在一些SELECT语句上引用resultMap。MyBatis的结果集映射 ResultMaps特性非常强大,你可以使用它将简单的SELECT语句映射到复杂的一对一、一对多关系的SELECT语句上。
3.3.1 简单ResultMap
一个映射了查询结果和Student这个JavaBean的简单的resultMap定义如下:
<resultMap id="StudentResult" type="com.briup.pojo.Student">
<id property="studId" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<result property="phone" column="phone" />
</resultMap>
<select id="findAllStudents" resultMap="StudentResult">
SELECT * FROM STUDENTS
</select>
<select id="findStudentById" parameterType="int" resultMap="StudentResult">
SELECT * FROM STUDENTS WHERE STUD_ID=#{studId}
</select>
resultMap的id值应该在此名空间内是唯一的,并且type属性是完全限定类名或者是返回类型的别名。
<result>子元素被用来将一个resultset列映射到JavaBean的一个属性中。
<id>元素和<result>元素功能相同,不过<id>它被用来映射到唯一标识属性,用来区分和比较对象(一般和主键列相对应)。
在<select>语句中,我们使用了resultMap属性,而不是resultType来引用StudentResult 映射。当<select>语句中配置了resutlMap属性MyBatis会使用此数据库列名与对象属性映射关系来填充JavaBean中的属性。
注意:resultType和resultMap二者只能用其一,不能同时使用。
<select>映射语句中如何将查询【一条】数据填充到HashMap中?
<select id="findStudentById" parameterType="int" resultType="map">
SELECT * FROM STUDENTS WHERE STUD_ID=#{studId}
</select>
在上述的<select>语句中,我们将resultType配置成map,即java.util.Hash Map的别名。在这种情况下,结果集的列名将会作为Map中的key值,而列值将作为Map的value值。
HashMap<String,Object> studentMap = sqlSession.selectOne("com.briup.mappers.StudentMapper.findStudentById", studId);
System.out.println("stud_id :"+studentMap.get("stud_id"));
System.out.println("name :"+studentMap.get("name"));
System.out.println("email :"+studentMap.get("email"));
System.out.println("phone :"+studentMap.get("phone"));
<select>映射语句中如何将查询【多条】数据填充到HashMap中?
<select id="findAllStudents" resultType="map">
SELECT STUD_ID, NAME, EMAIL, PHONE FROM STUDENTS
</select>
由于resultType=”map”和语句返回多行,则最终返回的数据类型应该是List<Hash Map<String,Object>,如下所示:
List<HashMap<String, Object>> studentMapList = sqlSession.select List("com.briup.mappers.StudentMapper.findAllStudents");
for(HashMap<String, Object> studentMap : studentMapList) {
System.out.println("studId :" + studentMap.get("stud_id"));
System.out.println("name :" + studentMap.get("name"));
System.out.println("email :" + studentMap.get("email"));
System.out.println("phone :" + studentMap.get("phone"));
}
3.3.2 拓展 ResultMap
我们可以从从另外一个<resultMap>,拓展出一个新的<resultMap>,这样,原先的属性映射可以继承过来,以实现:
<resultMap type="Student" id="StudentResult">
<id property="stud Id" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<result property="phone" column="phone" />
</resultMap>
<!-- Student类中又新增加了一个属性,该属性的类型是Address -->
<!-- 自定义类Address,类中也有多个属性,同时数据库中ADDRESSES表与其对应 -->
<resultMap type="Student" id="StudentWithAddressResult" extends="StudentResult">
<result property="address.addr Id" column="addr_id" />
<result property="address.street" column="street" />
<result property="address.city" column="city" />
<result property="address.state" column="state" />
<result property="address.zip" column="zip" />
<result property="address.country" column="country" />
</resultMap>
其中id为StudentWithAddressResult的resultMap拓展了id为StudentResult的resultMap
如果你只想映射Student数据,你可以使用id为StudentResult的resultMap,如下所示:
<select id="findStudentById" parameterType="int"
resultMap="StudentResult">
SELECT * FROM STUDENTS WHERE STUD_ID=#{stud Id}
</select>
如果你想将映射Student数据和Address数据,你可以使用id为StudentWithAddressResult的 resultMap:
<select id="selectStudentWithAddress" parameterType="int"
resultMap="StudentWithAddressResult">
SELECT STUD_ID, NAME, EMAIL, PHONE, A.ADDR_ID, STREET, CITY,
STATE, ZIP, COUNTRY
FROM STUDENTS S LEFT OUTER JOIN ADDRESSES A ON
S.ADDR_ID=A.ADDR_ID
WHERE STUD_ID=#{studId}
</select>
注:该sql语句使用了连接查询中的左外连接
四、mybatis数据类型转换及自定义类型转换。
当MyBatis将一个Java对象作为输入参数执行INSERT语句操作时,它会创建一个PreparedStatement对象,并且使用setXXX()方式对占位符设置相应的参数值。这里,XXX可以是Int,String,Date 等 Java对象属性类型的任意一个。示例如下:
insert into student values(#{id},#{name},#{gender},#{birthday})
为执行这个语句,MyBatis将采取以下一系列动作:
创建一个有占位符的PreparedStatement接口,如下:
Prepared Statement pstmt = connection.prepare Statement
("INSERT INTO STUDENTS VALUES(?,?,?,?)");
检查Student对象的属性studId的类型,然后使用合适setXXX方法去设置参数值。这里studId是integer类型,所以会使用setInt()方法:pstmt.setInt(1,student.getStudId());
类似地,对于name和gender属性都是String类型MyBatis使用setString()方法设置参数。
至于birthday属性, MyBatis会使用setDate()方法设置birthday处占位符位置的值。
MyBaits会将java.util.Date类型转换为java.sql.Timestamp并设值:
pstmt.set Timestamp(4, new Timestamp((student.getBirthday()).getTime()));
但MyBatis是怎么知道对于Integer类型属性使用setInt()和String类型属性使用setString()方法呢?其实MyBatis是通过使用类型处理器typeHandlers来决定这么做的。
MyBatis对于以下的类型使用内建的类型处理器:所有的基本数据类型、基本类型的包裹类型、 byte[]、java.util.Date、java.sql.Date、java,sql.Time、java.sql.Timestamp、java枚举类型等。所以当MyBatis发现属性的类型属于上述类型,他会使用对应的类型处理器将值设置到PreparedStatement中,同样地,当从SQL结果集构建JavaBean时,也有类似的过程。
那如果我们给了一个自定义的对象类型,来存储到数据库呢?示例如下:假设表STUDENTS 有一个 Address 字段,类型为 VARCHAR(25),而 JavaBean Student有一个
java代码:
Address 类定义Student类型的 adres 属性。
public class Address {
private String contry;
private String province;
private String city;
public Address() {
}
public Address(String contry, String province, String city) {
super();
this.contry = contry;
this.province = province;
this.city = city;
}
public String getContry() {
return contry;
}
public void setContry(String contry) {
this.contry = contry;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
public class Student{
private Integer id;
private String name;
private String email;
private Address adres;
// Setters and getters
}
xml配置:
<insert id="insertStudent" parameter Type="Student">
insert into student values(#{id},#{name},#{gender},#{birthday},${adres})
</insert>
这里,adres 参数需要传递给#{adres};而 adres 对象是 Address 类型。然而,MyBatis 并不知道该怎样来处理这个类型的对象
为了让My Batis明白怎样处理这个自定义的Java对象类型,如Address,我们可以创建一个自定义的类型处理器,MyBatis提供了抽象类BaseTypeHandler<T> ,我们可以继承此类创建自定义类型处理器。
代码如下所示:
package com.briup.typeconvert;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.StringTokenizer;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import com.briup.bean.Address;
public class TypeConvert extends BaseTypeHandler<Address>{
@Override
public Address getNullableResult(ResultSet arg0, String arg1)
throws SQLException {
System.out.println("getNullableResult by columnName.......11111");
Address adres = getAddress(arg0.getString(arg1));
return adres;
}
@Override
public Address getNullableResult(ResultSet arg0, int arg1)
throws SQLException {
System.out.println("getNullableResult by columnIndex.......11111");
Address adres = getAddress(arg0.getString(arg1));
return adres;
}
@Override
public Address getNullableResult(CallableStatement arg0, int arg1)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setNonNullParameter(PreparedStatement arg0, int arg1,
Address arg2, JdbcType arg3) throws SQLException {
StringBuffer result = new StringBuffer();
result.append(arg2.getContry()).append(",");
result.append(arg2.getProvince()).append(",");
result.append(arg2.getCity());
arg0.setString(arg1, result.toString());
}
private Address getAddress(String value){
StringTokenizer st = new StringTokenizer(value,",");
return new Address(st.nextToken(),st.nextToken(),st.nextToken());
}
}
注意:我们使用ps.setString()和rs.getString()方法是因为在数据库的表中adres列是VARCHAR类型。
最后一旦我们实现了自定义的类型处理器,我们需要在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>
<type Handlers>
<typeHandler handler="com.briup.typeconvert.TypeConvert"
jdbcType="VARCHAR" javaType="com.briup.bean.Address"/>
</type Handlers>
</configuration>
注册TypeConvert后, MyBatis就能够将Address类型的对象值存储到VARCHAR类型的列上。