mybatis
1.如何批量插入数据
SQL层面
先复习一下单条/批量插入数据的sql语句怎么写:
1. 单条插入数据的写法:
INSERT INTO [表名]
([列名],[列名])
VALUES
([列值],[列值]))
2.一次性批量插入数据的sql语句的写法:
INSERT INTO [表名]
([列名],[列名])
VALUES
([列值],[列值])),
([列值],[列值])),
([列值],[列值]));
批量的好处:可以避免程序和数据库建立多次连接,从而增加服务器负荷。
MyBatis层面如何完成批量插入
MyBatis批量插入数据到数据库有两种方式:xml文件,注解,这里使用MySQL。
方法一:xml配置
最基础的是用mapping.xml配置的方式,包括以下两种具体方式:
1. mapping.xml中insert语句可以写成单条插入,在调用方循环1000次
<!-- 在外部for循环调用1000次 -->
<insert id="insert" parameterType="sdc.mybatis.test.Student">
insert into student (id, name, sex,
address, telephone, t_id
)
values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR},
#{sex,jdbcType=VARCHAR},
#{address,jdbcType=VARCHAR}, #{telephone,jdbcType=VARCHAR}, #{tId,jdbcType=INTEGER}
)
</insert>
2. mapping.xml中insert语句写成一次性插入一个1000的list
mapping.xml
<insert id="insertBatch" >
insert into student ( <include refid="Base_Column_List" /> )
values
<foreach collection="list" item="item" index="index" separator=",">
(null,#{item.name},#{item.sex},#{item.address},#{item.telephone},#{item.tId})
</foreach>
</insert>
参数解释:
collection:指定要遍历的集合;
表示传入过来的参数的数据类型。该参数为必选。要做 foreach 的对象,作为入参时,List 对象默认用 list 代替作为键,数组对象有 array 代替作为键,Map 对象没有默认的键;
item:将当前遍历出的元素赋值给指定的变量,然后用#{变量名},就能取出变量的值,也就是当前遍历出的元素;
index:索引,遍历list的时候index就是索引,遍历map的时候index表示的就是map的key,item就是map的值;
separator:每个元素之间的分隔符, select * from Emp where id in(1,2,3)相当于1,2,3之间的","
mapper接口中的使用:
public interface EmpMapper {
public List<Emp> getEmpsByConditionLike(@Param("list")List<Integer> ids);
}
方法二:注解
注解说明:
MyBatis提供用于插入数据的注解有两个:@insert,@InsertProvider,类似还有:@DeleteProvider@UpdateProvider和@SelectProvider;
作用:
用来在实体类的Mapper类里注解保存方法的SQL语句
区别:
@Insert是直接配置SQL语句,而@InsertProvider则是通过SQL工厂类及对应的方法生成SQL语句,这种方法的好处在于,我们可以根据不同的需求生产出不同的SQL,适用性更好。
使用:
@Insert
@Insert("insert into blog(blogId,title,author) values(#blogId,#title,#author)")
public boolean saveBlog(Blog blog);
@InsertProvider
在mapper接口中的方法上使用@InsertProvider注解:
@InsertProvider(type=ActivationProvider.class, method="insertAll")
boolean insertAll(@Param("list") List<String> codeArray);
参数解释:
type为工厂类的类对象,method为对应的工厂类中的方法,方法中的@Param("list")是因为批量插入传入的是一个list,但是Mybatis会将其包装成一个map,其中map的key为"list",value为传入的list。
工厂类ActivationProvider中的方法:
首先要map.get方法得到对应的list;
然后拼接insert语句,要生成的正确的sql语句的格式为:
INSERT INTO User (id, name) VALUES (null, #{list[0].name}), (null, #{list[1].name})[,(null, #{list[i].name})]
其中list[0]代表list中第0个元素
特别注意:
一定注意:注意Mapper到Provider参数类型的变化(List --> Map),Mapper中传入的List会被包裹在一个Map中传给Provider,而key就是在Mapper的@Param注解中指定的名称(默认为list)。在Provider方法中使用List<T> list = (List<T>) map.get(key);即可拿到我们传入的List。
否则会报错:
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'arg0' not found. Available parameters are [1, 0, param1, param2]
xml、注解方式区别:
1.foreach相当语句逐条INSERT语句执行,将出现如下问题:
a. mapper接口的isnert方法返回值将是最一条INSERT语句的操作成功的记录数目
(就是0或1),而不是所有INSERT语句的操作成功的总记录数目。
b. 当其中一条不成功时,不会进行整体回滚。
2.注解方式:当有一条插入不成功时,会整体回滚
2. foreach有什么关键字
<foreach> 元素主要用在构建 in 条件中,它可以在 SQL 语句中迭代一个集合。
<foreach> 元素的属性主要有 item、index、collection、open、separator、close。
item 表示集合中每一个元素进行迭代时的别名。
index 指定一个名字,用于表示在迭代过程中每次迭代到的位置。
open 表示该语句以什么开始。
separator 表示在每次进行迭代之间以什么符号作为分隔符。
close 表示以什么结束。
在使用 <foreach> 元素时,最关键、最容易出错的是collection属性,该属性是必选的,但在不同情况下该属性的值是不一样的,主要有以下3种情况:
如果传入的是单参数且参数类型是一个List,collection属性值为list。
如果传入的是单参数且参数类型是一个array数组,collection的属性值为array。
如果传入的参数是多个,需要把它们封装成一个Map,当然单参数也可以封装成Map。Map的key是参数名,collection属性值是传入的List或array对象在自己封装的Map中的key。
1)添加SQL映射语句
在 com.mybatis 包的 UserMapper.xml 文件中添加如下 SQL 映射语句:
<!--使用foreach元素查询用户信息-->
<select id="selectUserByForeach" resultType="com.po.MyUser" parameterType=
"List">
select * from user where uid in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
# {item}
</foreach>
</select>
2)添加数据操作接口方法
在 com.dao 包的 UserDao 接口中添加如下数据操作接口方法:
public List<MyUser> selectUserByForeach(List<Integer> listId);
3)调用数据操作接口方法
在 com.controller 包的 UserController 类中添加如下程序调用数据操作接口方法。
//使用foreach元素查询用户信息
List<Integer> listId=new ArrayList<Integer>();
listId.add(34);
listId.add(37);
List<MyUser> listByForeach = userDao.selectUserByForeach(listId);
System.out.println ("foreach元素================");
for(MyUser myUser : listByForeach) {
System.out.println(myUser);
}
3.如何自增主键
同一张student表,对于mysql,sql server,oracle中它们都是怎样创建主键的
在mysql中
create table Student(
Student_ID int(6) NOT NULL PRIMARY KEY AUTO_INCREMENT,
Student_Name varchar(10) NOT NULL,
Student_Age int(2) NOT NULL
);
insert into student(student_name,student_age) values('zhangsan',20);
在sql server中
create table Student(
Student_ID int primary key identity(1,1),
Student_Name varchar2(10) NOT NULL,
Student_Age number(2) NOT NULL
);
insert into student(student_name,student_age) values('zhangsan',20);
在oracle中
create table Student(
Student_ID number(6) NOT NULL PRIMARY KEY,
Student_Name varchar2(10) NOT NULL,
Student_Age number(2) NOT NULL
);
而oracle如果想设置主键自增长,则需要创建序列
CREATE SEQUENCE student_sequence
INCREMENT BY 1
NOMAXVALUE
NOCYCLE
CACHE 10;
insert into Student values(student_sequence.nextval,'aa',20);
如果使用了触发器的话,就更简单了
create or replace trigger student_trigger
before insert on student
for each row
begin
select student_sequence.nextval into :new.student_id from dual;
end student_trigger;
此时插入的时候触发器会帮你插入id
insert into student(student_name,student_age) values('wangwu',20);
至此,mysql,sql server,oracle中怎样创建表中的自增长主键都已完成。
看一看出oracle的主键自增较mysql和sql sever要复杂些,mysql,sqlserver配置好主键之后,插入时,字段和值一一对应即可,数据库就会完成你想做的,但是在oracle由于多了序列的概念,如果不使用触发器,oracle怎样实现主键自增呢?
<insert id="add" parameterType="Student">
<selectKey keyProperty="student_id" resultType="int" order="BEFORE">
select student_sequence.nextval from dual
</selectKey>
insert into student(student_id,student_name,student_age) values(#{student_id},#{student_name},#{student_age})
</insert>
或者
<insert id="save" parameterType="com.threeti.to.ZoneTO" >
<selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" >
SELECT SEQ_ZONE.CURRVAL AS id from dual
</selectKey>
insert into TBL_ZONE (ID, NAME ) values (SEQ_ZONE.NEXTVAL, #{name,jdbcType=VARCHAR})
</insert>
4.如何使用in
在SQL语法中如果我们想使用in的话直接可以像如下一样使用:
select * from HealthCoupon where useType in ( '4' , '3' )
但是如果在MyBatis中的使用in的话,像如下去做的话,肯定会报错:
Map<String, Object> selectByUserId(@Param("useType") String useType)
<select id="selectByUserId" resultMap="BaseResultMap" parameterType="java.lang.String">
select * from HealthCoupon where useType in (#{useType,jdbcType=VARCHAR})
</select>
其中useType="2,3";这样的写法,看似很简单,但是MyBatis不支持。但是MyBatis中提供了foreach语句实现IN查询,foreach语法如下:
foreach语句中, collection属性的参数类型可以是:List、数组、map集合
collection: 必须跟mapper.java中@Param标签指定的元素名一样
item: 表示在迭代过程中每一个元素的别名,可以随便起名,但是必须跟元素中的#{}里面的名称一样。
index:表示在迭代过程中每次迭代到的位置(下标)
open:前缀,sql语句中集合都必须用小括号()括起来
close:后缀
separator:分隔符,表示迭代时每个元素之间以什么分隔
正确的写法有以下几种写法:
(一)、selectByIdSet(List idList)
如果参数的类型是List, 则在使用时,collection属性要必须指定为 list
List<User> selectByIdSet(List idList);
<select id="selectByIdSet" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List" />
FROM t_user
WHERE id IN
<foreach collection="list" item="id" index="index" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
(二)、List<User> selectByIdSet(String[] idList)
如果参数的类型是Array,则在使用时,collection属性要必须指定为 array
List<User> selectByIdSet(String[] idList);
<select id="selectByIdSet" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List" />
FROM t_user
WHERE id IN
<foreach collection="array" item="id" index="index" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
(三)、参数有多个时
当查询的参数有多个时,有两种方式可以实现,一种是使用@Param("xxx")进行参数绑定,另一种可以通过Map来传参数。
3.1 @Param("xxx")方式
List<User> selectByIdSet(@Param("name")String name, @Param("ids")String[] idList);
<select id="selectByIdSet" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List" />
FROM t_user
WHERE name=#{name,jdbcType=VARCHAR} and id IN
<foreach collection="idList" item="id" index="index"
open="(" close=")" separator=",">
#{id}
</foreach>
</select>
3.2 Map方式
Map<String, Object> params = new HashMap<String, Object>(2);
params.put("name", name);
params.put("idList", ids);
mapper.selectByIdSet(params);
<select id="selectByIdSet" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from t_user where
name = #{name}
and ID in
<foreach item="item" index="index" collection="idList" open="(" separator="," close=")">
#{item}
</foreach>
</select>