mybatis_day2

二 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类型的列上。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值