Mybatis学习笔记-三、使用XML配置SQL映射器

三、使用XML配置SQL映射器

关系型数据库和SQL是经受时间考验和验证的数据存储机制。和其他的ORM 框架如Hibernate不同,MyBatis鼓励开发者可以直接使用数据库,而不是将其对开发者隐藏,因为这样可以充分发挥数据库服务器所提供的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" resultType="Student"> 
	select stud_id as studId, name, email, dob  
	from students where stud_id=#{studId} 
  </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语句,但是这种方式容易出错。你需要检查映射器配置文件中的定义,以保证你的输入参数类型和结果返回类型是有效的。

【重点部分:】
MyBatis通过使用映射器Mapper接口提供了更好的调用映射语句的方法。一旦我们通过映射器配置文件配置了映射语句,我们可以创建一个完全对应的一个映射器接口,xml映射文件中的namespace属性值和映射接口的全限定名需要保持一致。映射器接口中的方法签名也跟映射器配置文件中完全对应:方法名和配置文件中id值一致;方法参数类型和parameterType属性值一种;方法返回值类型和returnType属性值一致。

上述的StudentMapper.xml文件,我们可以创建一个映射器接口StudentMapper.java如下:

package com.briup.mappers; 
public interface StudentMapper{ 
	Student findStudentById(Integer id); 
} 

在Student Mapper.xml映射器配置文件中,其名空间namespace应该跟StudentMapper接口的全限定名保持一致。另外,StudentMapper.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 插入语句

一个INSERT语句可以在标签元素在映射器XML配置文件中配置,如下所示:

<insert id="insertStudent" parameterType="Student"> 
	INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL, PHONE) VALUES(#{studId},#{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 insertStudent(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)来生成主键值。假设我们有一个名为my_seq的序列来生成SUTD_ID主键值。使用如下代码来生成主键:

drop sequence my_seq;
create sequence my_seq;

<insert id="insertStudent" parameterType="Student"> 
	<selectKey keyProperty="studId" resultType="int" order="BEFORE"> 
		SELECT my_seq.nextval FROM DUAL 
	</selectKey> 
	INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL, PHONE) 
		VALUES(#{studId},#{name},#{email},#{phone}) 
</insert> 

这里我们使用了子元素来生成主键值,并将值保存到Student对象的studId 属性上。属性order=“before”表示MyBatis将取得序列的下一个值作为主键值,并且在执行INSERT语句之前将值设置到studId属性上。

	注:SelectKey需要注意order属性,像MySQL、SQLServer等一类支持自动增长类型的数据库中,order需要设置为after才会取到正确的值。
	像Oracle这样取序列的情况,需要设置为before,否则会报错。

3.2.2 UPDATE 更新语句

一个UPDATE SQL语句可以在元素在映射器XML配置文件中配置,如下所示:

<update id="updateStudent" parameterType="Student"> 
	UPDATE STUDENTS SET NAME=#{name}, EMAIL=#{email}, PHONE=#{phone} 
	WHERE STUD_ID=#{studId} 
</update> 

我们可以如下调用此语句:

int noOfRowsUpdated = sqlSession.update("com.briup.mappers.StudentMapper.updateStudent", student); 

sqlSession.update()方法返回执行UPDATE语句之后影响的行数。

如果不使用名空间(namespace)和语句id来调用映射语句,你可以通过创建一个映射器Mapper接口,并以类型安全的方式调用方法,如下所示:

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语句可以在元素在映射器XML配置文件中配置,如下所示

<delete id="deleteStudent" parameterType="int"> 
   DELETE FROM STUDENTS WHERE STUD_ID=#{id} 
</delete> 

我们可以如下调用此语句:

int studId = 1; 
int noOfRowsDeleted = sqlSession.delete("com.briup.mappers.StudentMapper.deleteStudent", 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查询结果到java的各种类型。
让我们看看一个简单的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自动对java对象中和列名匹配的属性进行填充。这就是为什么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,以及(SortedSet)。MyBatis 根据集合的类型,会采用适当的集合实现,如下所示:
对于List,Collection,Iterable类型,MyBatis将返回java.util.ArrayList 
对于Map类型,MyBatis 将返回java.util.HashMap  
对于Set类型,MyBatis 将返回java.util.HashSet 
对于SortedSet类型,MyBatis将返回java.util.TreeSet 

3.3 结果集映射 ResultMaps

ResultMaps被用来将SELECT语句的结果集映射到java对象的属性中。我们可以定义结果集映射ResultMaps并且在一些SELECT语句上引用resultMap。MyBatis的结果集映射 ResultMaps特性非常强大,你可以使用它将简单的SELECT语句映射到复杂的一对一、一对多关系的SELECT语句上。

3.3.1 简单ResultMap

一个映射了查询结果为Student类型的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属性是完全限定类名或者是返回类型的别名。
子元素被用来将一个resultset列映射到对象的一个属性中。
元素和元素功能相同,不过它被用来映射到唯一标识属性,用来区分和比较对象(一般和主键列相对应)。
在语句中,我们使用了resultMap属性,而不是resultType属性。当语句中配置了resutlMap属性,MyBatis会使用表中的列名与对象属性 【映射关系】 来填充对象中的属性值。

注意:resultType和resultMap二者只能用其一,不能同时使用。

映射语句中如何将查询【一条】数据填充到HashMap中?

<select id="findStudentById" parameterType="int" resultType="map"> 
	SELECT * FROM STUDENTS WHERE STUD_ID=#{studId} 
</select> 

在上述的语句中,我们将resultType配置成map,即java.util.HashMap的别名。在这种情况下,结果集的列名将会作为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"));

映射语句中如何将查询【多条】数据填充到HashMap中?

<select id="findAllStudents" resultType="map"> 
	SELECT STUD_ID, NAME, EMAIL, PHONE FROM STUDENTS 
</select> 

由于resultType=”map”和语句返回多行,则最终返回的数据类型应该是List<Map<String,Object>>,如下所示:

List<Map<String, Object>> studentMapList = sqlSession.select List("com.briup.mappers.StudentMapper.findAllStudents"); 
for(Map<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")); 
} 

其他实例1:

<select id="findAllStudents_student" resultType="Student">
	SELECT STUD_ID AS STUDID,NAME,EMAIL,DOB
	FROM STUDENTS
</select>

对应的接口中的方法,你写什么类型的集合,Mybatis就给你返回什么类型的集合,但是要注意使用SortedSet的时候,Student类需要实现Comparable接口,否则是不能进行排序的
例如:

public List<Student> findAllStudents_List();
//或者
public Set<Student> findAllStudents_Set();
//或者
public SortedSet<Student> findAllStudents_SortedSet();

其他实例2:

<select id="findAllName_list" resultType="String">
	SELECT NAME
	FROM STUDENTS
</select>

对应的接口中的方法: 把查询到所有名字都放到List集合中并返回

public List<String> findAllName_list()

其他实例3:

<select id="findCount_int" resultType="int">
	SELECT count(*)
	FROM STUDENTS
</select>

对应的接口中的方法: 把查询到的这个值直接返回

public int findCount_int();

3.3.2 拓展 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.addrId" 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语句使用了连接查询中的左外连接,也可以使用等值连接
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值