MyBatis(3)——Mapper映射文件_参数处理

本文详细介绍MyBatis框架中的增删改查操作实现方式,包括INSERT、DELETE、UPDATE、SELECT语句的XML映射配置,以及如何处理不同类型的参数,如单个参数、多个参数、POJO、Map和自定义TO对象。此外,还深入探讨了自增主键的处理策略和参数解析的源码分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、增删改查

1、INSERT

<insert id="insert" parameterType="com.starfall.mybaits.entity.Employee">
	insert into employee
	(employee_id, first_name, last_name,
	email, phone_number, hire_date,
	job_id, salary, manager_id,
	department_id)
	values
	(#{employeeId,jdbcType=INTEGER}, #{firstName,jdbcType=VARCHAR},
	#{lastName,jdbcType=VARCHAR},
	#{email,jdbcType=VARCHAR},
	#{phoneNumber,jdbcType=VARCHAR}, #{hireDate,jdbcType=DATE},
	#{jobId,jdbcType=VARCHAR}, #{salary,jdbcType=DECIMAL},
	#{managerId,jdbcType=INTEGER},
	#{departmentId,jdbcType=INTEGER})
</insert>

2、DELETE

<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
	delete from
	employee
	where employee_id = #{employeeId,jdbcType=INTEGER}
</delete>

3、UPDATE

<update id="updateByPrimaryKey" parameterType="com.starfall.mybaits.entity.Employee">
	update employee
	set first_name = #{firstName,jdbcType=VARCHAR},
	last_name =
	#{lastName,jdbcType=VARCHAR},
	email = #{email,jdbcType=VARCHAR},
	phone_number = #{phoneNumber,jdbcType=VARCHAR},
	hire_date =
	#{hireDate,jdbcType=DATE},
	job_id = #{jobId,jdbcType=VARCHAR},
	salary =
	#{salary,jdbcType=DECIMAL},
	manager_id = #{managerId,jdbcType=INTEGER},
	department_id = #{departmentId,jdbcType=INTEGER}
	where employee_id =
	#{employeeId,jdbcType=INTEGER}
</update>

4、SELECT

<select id="selectEmployee" resultType="com.starfall.mybaits.entity.Employee">
	SELECT
		employee_id AS employeeId,
		first_name AS firstName,
		last_name AS lastName,
		email AS email,
		phone_number AS phoneNumber,
		hire_date AS hireDate,
		job_id AS jobId,
		salary AS salary,
		manager_id AS managerId,
		department_id AS departmentId
	FROM
		employee
	WHERE
		employee_id = #{employeeId}
</select>

5、测试

@Test
public void test03() throws IOException{
	SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
	//1、获取到的SqlSession不会自动提交数据
	SqlSession openSession = sqlSessionFactory.openSession();
	try{
		EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
		//测试添加
		Employee employee = new Employee(null, "jerry4",null, "1");
		mapper.addEmp(employee);
		System.out.println(employee.getId());
		//2、手动提交数据
		openSession.commit();
	}finally{
		openSession.close();
	}
}

mybatis允许增删改直接定义以下类型返回值:Integer、Long、Boolean、void

需要手动提交数据:

  • sqlSessionFactory.openSession();===》手动提交:openSession.commit();
  • sqlSessionFactory.openSession(true);===》自动提交

二、自增主键

1、MySQL、SQL Server获取自增主键

mysql支持自增主键,自增主键值的获取,mybatis也是利用statement.getGenreatedKeys();

useGeneratedKeys="true";使用自增主键获取主键值策略
 keyProperty;指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给javaBean的哪个属性。

<insert id="insertEmployee" 
	parameterType="com.starfall.mybaits.entity.Employee"
	useGeneratedKeys="true" keyProperty="employeeId">
	insert into employee(first_name,last_name,email)
	values(#{firstName},#{lastName},#{email})
</insert>

2、Oracle获取非自增主键

(1)Oracle使用序列来模拟自增:

--下一个序列值
select employees_seq.nextval from dual
--当前序列值
select employees_seq.currval from dual

(2)mapper写法:

keyProperty:  查出的主键值封装给javaBean的哪个属性
order :    "BEFORE":当前sql在插入sql之前运行
              "AFTER":当前sql在插入sql之后运行
resultType:  查出的数据的返回值类型

AFTER示例:

<insert id="insertEmployee" parameterType="com.starfall.mybaits.entity.Employee"
databaseId="oracle">
	<selectKey keyProperty="employeeId" order="BEFORE" resultType="Integer">
		<!-- 编写查询主键的sql语句 -->
		select employees_seq.nextval from dual 
	</selectKey>
	insert into employees(employee_id,first_name,last_name,email)
	values(#{employeeId},#{firstName},#{lastName},#{email})
</insert>

AFTER示例:

<insert id="insertEmployee" parameterType="com.starfall.mybaits.entity.Employee"
databaseId="oracle">
	<selectKey keyProperty="employeeId" order="AFTER" resultType="Integer">
		<!-- 编写查询主键的sql语句 -->
		select employees_seq.currval from dual 
	</selectKey>
	insert into employees(employee_id,first_name,last_name,email)
	values(employees_seq.nextval,#{firstName},#{lastName},#{email})
</insert>

(3)BEFORE与AFTER区别:

BEFORE运行顺序:
            先运行selectKey查询id的sql;查出id值封装给javaBean的id属性
            在运行插入的sql;就可以取出id属性对应的值
            BEFORE使用序列为:select employees_seq.nextval from dual

AFTER运行顺序:
            先运行插入的sql(从序列中取出新值作为id);
            再运行selectKey查询id的sql;
            AFTER使用序列为:select employees_seq.currval from dual

建议使用BEFORE,因为在批量插入的时候,AFTER取出的序列可能是最后一个序列,而不是最近插入的一条记录的序列。

三、参数处理

1、单个参数

单个参数:mybatis不会做特殊处理。#{参数名/任意名}:取出参数值。

不需要使mapper接口中的参数名和mapper文件中的#{参数名}相同。

2、多个参数

(1)示例

接口:Employee selectEmployeeByIdAndLastName(Integer employeeId, String lastName);

SQL:

<select id="selectEmployeeByIdAndLastName" resultType="com.starfall.mybaits.entity.Employee">
	SELECT
		*
	FROM
		employee
	WHERE
		employee_id = #{employeeId}
	AND last_name = #{lastName}
</select>

异常:
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'employeeId' not found. Available parameters are [arg1, arg0, param1, param2]

原因:多个参数会被封装成 一个map,
                   key:param1...paramN,或者参数的索引也可以
                   value:传入的参数值
                   #{}就是从map中获取指定的key的值;

(2)命名参数

明确指定封装参数时map的key;@Param("id")

public Employee getEmpByIdAndLastName(@Param("id")Integer id,@Param("lastName")String lastName);

3、参数类型

(1)、POJO

如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo;#{属性名}:取出传入的pojo的属性值

(2)、Map

如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便,我们也可以传入map;#{key}:取出map中对应的值

(3)、TO

如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)数据传输对象
  Page{
      int index;
      int size;
  }

4、其他特殊场景

(1)、public Employee getEmp(@Param("id")Integer id,String lastName);
    取值:id==>#{ id / param1 }   lastName==>#{ param2 }

(2)、public Employee getEmp(Integer id,@Param("e")Employee emp);
    取值:id==>#{ param1 }    lastName===>#{ param2.lastName/e.lastName }

(3)、public Employee getEmpById(List<Integer> ids);
    取值:取出第一个id的值:   #{ list[0] }

##特别注意:如果是Collection(List、Set)类型或者是数组,也会特殊处理。也是把传入的list或者数组封装在map中。
            key:Collection(collection),List(list), 数组(array)

5、源码分析参数处理

Debugger的源码:

Employee employee = mapper.selectEmployeeByIdAndLastName(100, "King");

Employee selectEmployeeByIdAndLastName(@Param("employeeId") Integer employeeId, @Param("lastName") String lastName);

5.1 在调用处打上debugger断点

5.2 step into 进入mapper.selectEmployeeByIdAndLastName方法:

调用MapperProxy<T>.invoke(Object, Method, Object[]) 方法:

其中,invoke方法的参数:

proxy:org.apache.ibatis.binding.MapperProxy@609bcfb6。mapper的代理对象。

method:mapper接口中的方法全类名。

public abstract com.starfall.mybaits.entity.Employee com.starfall.mybaits.dao.EmployeeMapper.selectEmployeeByIdAndLastName(java.lang.Integer,java.lang.String)

args:mapper接口中的方法的参数

5.2.1 进入第58行,获取mapperMethod:

第63行,首先从缓存中getmapperMethod,如果没有则进入第65行,根据mapper接口(反射对象)、mapper接口中的方法、sqlSession构造一个。

进入MapperMethod构造器:

其中有两个方法:获取SqlCommand对象和method对象。

5.2.1.1 第49行:new SqlCommand构造器,获取command对象:属性name为查询方法的全类名,type为SELECT。

5.2.1.2 第50行:new MethodSignature构造器,获取method对象:进入构造器查看:

整个MethodSignature构造器用了获取该查询方法的信息,例如返回类型(returnType)、是否返回void(returnsVoid)。

注意看第295行:ParamNameResolver参数解析器进入其中看如何解析查询方法参数:

5.2.1.2.1 第54行,paramTypes:通过反射获取方法参数的类型数组。

5.2.1.2.2 第55行,paramAnnotations:通过反射获取方法参数的注解数组。

5.2.1.2.3 第59行开始,遍历参数数组

               第65行开始,遍历该参数的注解数组,获取注解的value值,并且hasParamAnnotation属性设为true.

               注意第75行,name = getActualParamName(method, paramIndex); 如果mybatis全局配置文件settings标签中配置了useActualParamName属性(允许使用方法签名中的名称作为语句参数名称)则name直接为参数的名称。

5.2.1.2.4 第83行,获取的name和参数下标索引put到map中。

5.2.1.2.5 第85行,最后返回names属性的map:private final SortedMap<Integer, String> names;

5.2.2 在5.2.1中获取完mapperMethod后,继续执行mapperMethod.execute(sqlSession, args);


5.2.2.1 在mapperMethod.execute()方法中,主要看下面代码:method.convertArgsToSqlCommandParam(args);

5.2.2.1.1 进入method.convertArgsToSqlCommandParam(args);

method对象是由 5.2.1.2 第50行:new MethodSignature构造器获取,进入MethodSignature类,发现是调用paramNameResolver.getNamedParams(args);方法。

5.2.2.1.1.1 进入paramNameResolver.getNamedParams(args);方法

注意两个属性:names和hasParamAnnotation,是在上方5.2.1中ParamNameResolver参数解析器中设置初始化的。names的值可以参考上方第5.2.1.2.5步。

a、注意第118行,如果参数没有注解,并且参数只有一个,则返回第一个key:args[0]:单个参数直接返回。

b、第123开始行遍历names集合:{0=employeeId, 1=lastName}

将names的value作为key(employeeId),args方法参数对应下标元素的值作为value(100)

c、第129行,同时添加mybatis默认的参数:param1=100

d、最后返回param的map集合:

至此参数解析完毕。

6、参数值的获取

(1)#{ }和${ }

1、都可以可以获取map中的值或者pojo对象属性的值

2、#{}:是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入
      ${}:取出的值直接拼装在sql语句中;会有安全问题;

示例:

select * from tbl_employee where id=${id} and last_name=#{lastName}
Preparing: select * from tbl_employee where id=2 and last_name=?

(2)${ }使用场景

原生jdbc不支持占位符的地方我们就可以使用${}进行取值,比如分表、排序。。。;按照年份分表拆分。

select * from ${year}_salary where xxx;
select * from tbl_employee order by ${f_name} ${order}

(3)#{}:更丰富的用法

1、规定参数的一些规则

javaType、 jdbcType、 mode(存储过程)、 numericScale(数值类型,设置小数点后保留的位数)、resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能)

2、jdbcType

jdbcType通常需要在某种特定的条件下被设置。

在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错);

JdbcType OTHER:无效的类型;因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,oracle不能正确处理;

由于全局配置中:jdbcTypeForNull配置默认为 OTHER,而oracle不支持OTHER;
两种办法
        1、在指定的列中指定jdbcType类型:#{email,jdbcType=NULL},或者为#{email,jdbcType=VARCHAR}。不为默认的OTHER。
        2、在全局配置文件中设置空值指定 JDBC 类型:jdbcTypeForNull=NULL
            <setting name="jdbcTypeForNull" value="NULL"/>

上一章:MyBatis(2)——全局配置文件

下一章:MyBatis(4)——Mapper映射文件_SELECT

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值