MyBatis框架(03)——Mapper动态代理


版权声明

  • 本文原创作者:清风不渡
  • 博客地址:https://blog.csdn.net/WXKKang

开篇浅谈

  自从学习了框架之后,越来越感觉自己不中用了,很多的东西都是在使用前辈们写好的代码,其实我们现在所学的任何东西都是,在赞叹前辈们精深的技术之余也越来越感觉到自己所知技术的浅薄,什么时候自己也能写出这样精妙的代码呢

一、实现原理

  Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法
Mapper接口开发需要遵循以下规范:
  1、Mapper.xml 文件中的namespace与mapper接口的类路径相同。
  2、Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
  3、Mapper 接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
  4、Mapper接口方法的返回参数类型和mapper.xml中定义的每个sq|的resultType的类型相同

二、具体实现

mybatis-config代码:

<?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>
		<!--和spring整合后,environments配置将废除 -->
		<environments default="development">
			<environment id="development">
				<!-- 使用JDBC事务管理 -->
				<transactionManager type="JDBC" />
				<!-- 数据库连接池 -->
				<dataSource type="POOLED">
					<property name="driver" value="com.mysql.jdbc.Driver" />
					<property name="url"
						value="jdbc:mysql://localhost:3306/demo?characterEncoding=utf-8" />
					<property name="username" value="root" />
					<property name="password" value="root" />
				</dataSource>
			</environment>
		</environments>
		<mappers>
			<mapper resource="mappers/usermapper.xml"/>
		</mappers>
	</configuration> 

usermapper.xml代码:
注意: 要将namespace的值改为代理接口的全路径,如下:

<?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="cn.com.demo.mapper.UserMapper">
	<!-- 根据id获取用户信息 -->
	<select id="selectUserById" parameterType="java.lang.String" resultType="cn.com.demo.pojo.User">
		select * from user where userId = #{userId}
	</select>
	<!-- 根据username模糊查询用户 -->
	<select id="selectUserByUsername" parameterType="java.lang.String" resultType="cn.com.demo.pojo.User">
		select * from user where username like '%${value}%'
	</select>
	
	<!-- 添加用户 -->
	<insert id="insertUser" parameterType="cn.com.demo.pojo.User">
		insert into user(userId,username,password,age) values(#{userId},#{username},#{password},#{age})
	</insert>
	
	<!-- 修改用户信息 -->
	<update id="updateUser" parameterType="cn.com.demo.pojo.User">
		update user set userId=#{userId},username=#{username},password=#{password},age=#{age} where userId=#{userId}
	</update>
	
	<!-- 删除用户信息 -->
	<delete id="deleteUserById" parameterType="java.lang.String">
		delete from user where userId=#{userId}
	</delete>
</mapper>

动态代理接口UserMapper代码:
注意: 方法名和返回值以及参数要和对应的配置文件节点的id、resultType、parameterType一致,如下:

package cn.com.demo.mapper;

import java.util.List;

import cn.com.demo.pojo.User;

public interface UserMapper {
	//根据id查询单个用户
	User selectUserById(String userId);
	//根据username模糊查询多个用户
	List<User> selectUserByUsername(String username);
	//添加用户信息
	void insertUser(User user);
	//修改用户信息
	void updateUser(User user);
	//通过id删除用户信息
	void deleteUserById(String userId);
}

测试类代码:

package cn.com.demo.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import cn.com.demo.mapper.UserMapper;
import cn.com.demo.pojo.User;

public class Test2 {
	public static void main(String[] args) {
		SqlSessionFactory sqlSessionFactory = factory();
		SqlSession session = sqlSessionFactory.openSession();
		UserMapper mapper = session.getMapper(UserMapper.class);
		//通过userId查询单个用户信息
//		User user = mapper.selectUserById("S101");
//		System.out.println(user);
		
		//通过username模糊查询多个用户信息
//		List<User> list = mapper.selectUserByUsername("k");
//		System.out.println(list);
		
		//添加用户信息信息
//		User user = new User("S105", "TOM", "123", 20);
//		mapper.insertUser(user);
//		session.commit();
		
		//修改用户信息
//		User user = new User("S105", "TOOM", "XXX", 19);
//		mapper.updateUser(user);
//		session.commit();
		
		//删除用户信息
		mapper.deleteUserById("S105");
		session.commit();
	}
	
	public static SqlSessionFactory factory(){
		SqlSessionFactory sqlSessionFactory = null;
		try {
			String resource = "mybatis-config.xml";
			InputStream inputStream = Resources.getResourceAsStream(resource);
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return sqlSessionFactory;
	}
}

小结:

  • selectOne和selectList
      动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。
  • namespace
      mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可以使用pojo包装对象或map对象,保证dao的通用性。

三、mybatis-config.xml文件

  我们可以使用properties文件将其中的environments(环境集合属性对象)内的连接数据库部分的主要信息写在外面然后在里面加以引用是不是就方便了很多呢?来试一下吧:
db.properties文件:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/demo
jdbc.username=root
jdbc.password=root

那么我们在mybatis-config.xml中如何引用呢——EL表达式:

<?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>
		<!--和spring整合后,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>
		<mappers>
			<mapper resource="mappers/usermapper.xml"/>
		</mappers>
	</configuration> 

注意: MyBatis 将按照下面的顺序来加载属性:

  • 在properties元素体内定义的属性首先被读取。
  • 然后会读取 properties元素中 resource或url加载的属性, 它会覆盖已读取的同名属性。
  • 最后读取 parameterType传递的属性,它会覆盖已读取的同名属性。
      因此,通过parameterType传递的属性具有最高优先级,resource 或url 加载的属性次之, .
      最低优先级的是properties 元素体内定义的属性。

四、mappers—映射器

  mapper配置的几种方法:

  • <mapper resource=" ">:使用相对类路径的资源
  • <mapper url=" ">:使用完全限定路径
  • <mapper class=" ">:使用mapper接口类路径
    注意: 这种方式要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
  • <package name=" ">:注册指定包下的所有mapper接口
    注意: 这种方式要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。

五、mapper.xml映射文件

  Mapper.xml映射文件中定义了操作数据库的sq|,每个sql是一个statement,映射文件是mybatis的核心。

1、parameterType(输入类型)

(1)#{}与${}

  #{}实现的是向prepareStatement中的预处理语句中设置参数值,sql 语句中#{}表示一一个占位符即?,如下:

<!-- 根据id获取用户信息 -->
<select id="selectUserById" parameterType="java.lang.String" resultType="cn.com.demo.pojo.User">
	select * from user where userId = #{userId}
</select>

  使用占位符#{}可以有效防止sql注入,在使用时不需要关心参数值的类型,mybatis 会自动进行java类型和jdbc类型的转换。#{}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
  ${}和#{}不同,通过${}可以将parameterType传入的内容拼接在sql 中且不进行jdbc类型转换,${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,${}括号中只能是value。使用${}不能防止sql注入,但是有时用${}会非常方便,如下的例子:

<!-- 根据username模糊查询用户 -->
<select id="selectUserByUsername" parameterType="java.lang.String" resultType="cn.com.demo.pojo.User">
	select * from user where username like '%${value}%'
</select>

  如果本例子使用#{}则传入的字符串中必须有%号,而%是人为拼接在参数中,显然有点麻烦,而采用${}在sql中拼接为%的方式则在调用mapper接口传递参数就方便很多。
  再比如order by排序,如果将排序方式(asc/desc) 通过参数传入sql,,应该写为:ORDER BY columnName ${value}
  如果使用#{}将无法实现此功能。

2、resultType(输出类型)

  1、输出pojo对象和输出pojo列表在sq|中定义的resultType是一样的。
  2、返回单个pojo对象要保证sq|查询出来的结果集为单条,内部使用session.selectOne方法调用,mapper接口使用pojo对象作为方法返回值。
  3、返回pojo列表表示查询出来的结果集可能为多条,内部使用session.selectList方法,mapper
  4、接口使用List对象作为方法返回值。
  5、输出hashmap
  输出pojo对象可以改用hashmap输出类型,将输出的字段名称作为map的key,value为字段值。
  注意: 输出hashmap,要求查询结果只能是一条或者 0条记录(内部使用的是session.selectOne方法)。如果没有查询结果,那么返回一个nul。

3、resultMap

  resultType可以指定pojo将查询结果映射为pojo,但需要pojo的属性名和sql查询的列名一致方可映射成功。
  如果sql查询字段名和pojo的属性名不一致,可以通过resultMap将字段名和属性名作一个对应关系,resultMap实质上还需要将查询结果映射到pojo对象中。
  resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。
  注:使用前首先要在上面定义map,如下面的例子:

<resultMap type="cn.com.demo.pojo.User" id="usermapper">
		<id column="id_" property="userId"/>
		<result column="name_" property="username"/>
		<result column="psw_" property="password"/>
		<result column="age_" property="age"/>
</resultMap>
	
		
<!-- 根据id获取用户信息 -->
<select id="selectUserById" parameterType="java.lang.String" resultMap="usermapper">
	select userId id_,username name_,password psw_,age age_ from user where userId = #{userId}
</select>

  <id />:此属性表示查询结果集的唯一标识,非常重要。如果是多个字段为复合唯一- 约束则定义多个<id/>。
  Property:表示person类的属性。
  Column:表示sq|查询出来的字段名。
  Column和property:放在一块儿表示将sql查询出来的字段映射到指定的pojo类属性上。
  <result/>:普通结果,即pojo的属性。

六、动态sql

  通过mybatis提供的各种标签方法实现动态拼接sql。

1、if

<!-- 传递pojo综合查询用户信息-->
<select id="findUserList" parameterType="cn.com.demo.pojo.User" resultType="cn.com.demo.pojo.User">
select * from user
where 1=1
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''"> 
and username like '%${username}%'
</if>
</select>

2、where

<select id="findUserList" parameterType="cn.com.demo.pojo.User" resultType="cn.com.demo.pojo.User">
select * from user
<where>
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''"> 
and username like '%${username}%'
</if>
</where>
</select>

<where/>可以自动处理第一个and

3、foreach

  如果我们想sql语句传入的参数是一个数组或者List,那么就需要我们去遍历它了,和上面一样,我们根据实例来学习它:
  如果说我们需要通过多个id查询多条用户信息,那么我们的sql语句有以下两种:

select * from user where userId="S101" or userId="S102" or userId="S103"

select * from user where userId IN ("S101","S102","S103")

  了解了它最基本的sql语句之后就变得简单许多了,让我们下面以第二种方式为例来写一下吧:

<select id="selectUserByList" parameterType="java.uril.List" resultType="cn.com.demo.pojo.User">
	select * from user
	<where>
		<if test="list!=null">
			<foreach collection="list" item="item" open="and id in(" separator="," close=")">
				#{item}
			</foreach>
		</if>
	</where>
</select>

  注: 如果List中是pojo那么#{value} 就是#{item.userId}了,这个上面有做说明的
  **如果是数组则这样写:**以数组中为pojo为例

<!--传递数组综合查询用户信息-->
<select id="selectUserByArray" parameterType="0bject[]" resultType="cn.com.demo.pojo.User">
	select * from user
	<where>
	<!--传递数组-->
		<if test="array!=null">
			<foreach collection="array" index="index" item="item" open="and id in( "separator=", "close=")">
			#{item.userId}
			</foreach>
		</if>
	</where>
</select>

  sql只接收一个数组参数,这时sql解析参数的名称mybatis固定为array,如果数组是通过一个pojo传递到sq|则参数的名称为pojo中的属性名。
  index:为数组的下标。
  item:为数组每个元素的名称,名称随意定义
  open:循环开始
  close:循环结束
  separator:中间分隔输出

3、sql片段

  Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sq|重用的目的,如下:

<!--传递pojo综合查询用户信息-->
<select id="findUserList" parameterType="cn.com.demo.pojo.User" resultType="cn.com.demo.pojo.User">
	select * from user
	<where>
		<if test="id!=null and id!=''"> 
		and id=#{id}
		</if>
		<if test="username!=null and username!=''">
		and username like '%${username }%'
		</if>
	</where>
</select>

我们可以将where中的代码抽取出来,使用的时候以include引用就好了,如下:

<sql id="query_user_where">
	<if test="id!=nulL and id!=''"> 
	and id=#{id}
	</if>
	<if test="username!=null and username!=' '">
	and username like '%${username }%'
	</if>
</sq1>

<select id="findUserList" parameterType="user" resultType="user">
	select * from user
	<where>
		<include refid="query_user_where"/>
	</where>
</select>

注意: 如果引用其它mapper.xml的sql片段,则在引用时需要加上namespace,如下:

<include refid= "namespace.sql片段"/>

  好啦,今天的学习就到这里吧!记录学习,记录生活,我还是那个java界的小学生,一起努力吧!!
  如果有什么缺陷欢迎各位看官评论探讨哟 ~ ~ 小生在此谢过了 ~ ~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值