mybatis动态sql-(6)-批量插入foreach与sql片段抽取

mysql批量插入与oracle批量插入不同

一、搭建数据库

tbl_employee表【d_id 是外键 对应 tbl_dept表】

id	last_name    gender	  email       d_id
1	   mike	       0      [email protected]      1
2          book        0      [email protected]    2
3          tom         1      [email protected]    2
4          jerry       1       [email protected]       1

tbl_dept表:

id    dept_name
1      开发部
2      测试部

二、编辑javabean

package com.mybatis.bean;

public class Employee {
	private Integer id;
	private String lastName;
	private String email;
	private String gender;
	private Department department;

	
	public Department getDepartment() {
		return department;
	}

	public void setDepartment(Department department) {
		this.department = department;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getGender() {
		return gender;
	}

	public void setGender(String gender) {
		this.gender = gender;
	}

	@Override
	public String toString() {
		return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender + "]";
	}
	
   /*****  关键是 这个地方啊 出了 问题 ****/
	public Employee() {
		super();
	}
	/*****/
	public Employee(Integer id,String lastName, String  email, String gender){
		this.id =id;
		this.lastName =lastName;
		this.email =email;
		this.gender =gender;
		
	}
	
	public Employee(Integer id,String lastName, String  email, String gender,Department dep){
		this.id =id;
		this.lastName =lastName;
		this.email =email;
		this.gender =gender;
		this.department =dep;
	}
	
//  历史教训 ,一开始 没有添加 无参构造函数,而添加了下面这个函数	
//	public Employee(Integer id,String lastName, String  email, String gender,int temp){
//		this.id =id;
//		this.lastName =lastName;
//		this.email =email;
//		this.gender =gender;
//		
//		System.out.println(" id:"+id+" lastName:"+lastName+" email:"+email+" gender:"+gender+" temp:"+temp);
//	}

}
package com.mybatis.bean;

import java.util.List;

public class Department {

	private Integer id;
	private String departmentName;
	private List<Employee> employees;

	public Department() {
		
	}
	
    public Department(int id){
    	this.id = id;
		
	}

	public List<Employee> getEmployees() {
		return employees;
	}

	public void setEmployees(List<Employee> employees) {
		this.employees = employees;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getDepartmentName() {
		return departmentName;
	}

	public void setDepartmentName(String departmentName) {
		this.departmentName = departmentName;
	}

	@Override
	public String toString() {
		return "Department [id=" + id + ", departmentName=" + departmentName + "]";
	}

}

 

三、编辑junit测试

@Test
public void testBatchSave() throws IOException{
	SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
	SqlSession openSession = sqlSessionFactory.openSession();
	try{
		EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL.class);
		List<Employee> emps = new ArrayList<>();
		emps.add(new Employee(null, "smith0x1", "[email protected]", "1" , new Department(1) ));
		emps.add(new Employee(null, "allen0x1", "[email protected]", "0" , new Department(1) ));
		mapper.addEmps(emps);
		openSession.commit();
	}finally{
		openSession.close();
	}
}

四、编辑SQl映射文件

1、mysql 版

 <!--MySQL下批量保存:可以foreach遍历   mysql支持values(),(),()语法-->
<!-- 批量保存 -->
 <!--public void addEmps(@Param("emps")List<Employee> emps);  -->
 <!--MySQL下批量保存:可以foreach遍历   mysql支持values(),(),()语法-->
<insert id="addEmps">
	 insert into tbl_employee(last_name,email,gender,d_id) 
	values
	<foreach collection="emps" item="emp" separator=",">
		(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.department.id})
	</foreach>
 </insert><!--   -->
	 
 <!-- 这种方式需要数据库连接属性allowMultiQueries=true;
	 这种分号分隔多个sql可以用于其他的批量操作(删除,修改) -->
 <!-- <insert id="addEmps">
	 <foreach collection="emps" item="emp" separator=";">
	 	insert into tbl_employee(last_name,email,gender,d_id)
	 	values(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.department.id})
	 </foreach>
 </insert> -->

mysql补充:在mysql配置时,我们可能需要配置 allowMultiQueries=true
jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true

若一个sql中通过分号分割(或包含)了多个独立sql的话,如:
select 'hello';select 'world'
就会报如下错:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select 'world'' at line 1
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)

当若显式设置allowMultiQueries为true的话,就可以正常执行不会报错.如下所示:
String url = "jdbc:mysql://localhost:3306?allowMultiQueries=true";

官方文档解释:
allowMultiQueries
Allow the use of ';' to delimit multiple queries during one statement (true/false), defaults to 'false', and does not affect the addBatch() and executeBatch() methods, which instead rely on rewriteBatchStatements.
Default: false
Since version: 3.1.1

2、oracle版

 <!-- Oracle数据库批量保存: 
	 Oracle不支持values(),(),()
	 Oracle支持的批量方式
	 1、多个insert放在begin - end里面
	 	begin
		     insert into employees(employee_id,last_name,email) 
		     values(employees_seq.nextval,'test_001','[email protected]');
		     insert into employees(employee_id,last_name,email) 
		     values(employees_seq.nextval,'test_002','[email protected]');
		end;
	2、利用中间表:
		insert into employees(employee_id,last_name,email)
		   select employees_seq.nextval,lastName,email from(
		          select 'test_a_01' lastName,'test_a_e01' email from dual
		          union
		          select 'test_a_02' lastName,'test_a_e02' email from dual
		          union
		          select 'test_a_03' lastName,'test_a_e03' email from dual
		   )	
	 -->

实际写法:

<insert id="addEmps" databaseId="oracle">
	 <!-- oracle第一种批量方式 -->
	 <!-- <foreach collection="emps" item="emp" open="begin" close="end;">
	 	insert into employees(employee_id,last_name,email) 
		      values(employees_seq.nextval,#{emp.lastName},#{emp.email});
	 </foreach> -->
	 	
	 <!-- oracle第二种批量方式  -->
	 insert into employees(
	 	<!-- 引用外部定义的sql -->
	 	<include refid="insertColumn">
	 		<property name="testColomn" value="abc"/>
	 	</include>
	 )
	 		<foreach collection="emps" item="emp" separator="union"
	 			open="select employees_seq.nextval,lastName,email from("
	 			close=")">
	 			select #{emp.lastName} lastName,#{emp.email} email from dual
	 		</foreach>
 </insert>

特别提醒一下:下面的引用其实就是 待插入的 各个字段啦。

<!-- 引用外部定义的sql -->
	 <include refid="insertColumn">
	 	<property name="testColomn" value="abc"/>
	 </include>

用法说明:不是只能用在批量插入中,也可以用在其他数据操作中,因为只是对SQL片段的抽取。

 <!-- 
	  抽取可重用的sql片段。方便后面引用 
	  1、sql抽取:经常将要查询的列名,或者插入用的列名抽取出来方便引用
	  2、include来引用已经抽取的sql:
	  3、include还可以自定义一些property,sql标签内部就能使用自定义的属性
	  		include-property:取值的正确方式${prop},
	  		#{不能使用这种方式}
          4、employee_id,last_name,email,${prop} 这个oracle插入 其实等价于【当然数据库有没有相应字段是另一回事,在这里只是用来演示】 employee_id,last_name,email,abc
          5、_databaseId 是 mybatis 内置参数,和在 全局配置文件中,标记的数据库 名是 一样的
-->
  <sql id="insertColumn">
	  	<if test="_databaseId=='oracle'">
	  		employee_id,last_name,email,${prop}
	  	</if>
	  	<if test="_databaseId=='mysql'">
	  		last_name,email,gender,d_id
	  	</if>
 </sql>

 

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments