mybatis批量新增系列之无主键的表的批量新增

来源:互联网 发布:工作计划软件 编辑:程序博客网 时间:2024/06/06 01:37

在前面写了几篇批量新增的。虽然可以操作,但是很麻烦,每当对一个表进行批量插入的时候,就要在mapper文件中写一个语句,如果表的列名是有规律的还好说,写一个方法就可以拼接处sql语句,但是在很多时候,表中的列名往往是不规律的,故而很容易写错表名导致整个程序出错。最近查了下资料,终于找到了解决方案。
通过多次写sql,发现批量新增的语句中主要由两个sql语句组成
<insert id="batchDemo" parameterType="List">
    <![CDATA[
          ${insertSql}
    ]]>
    <foreach collection="list" item="obj" index="index" separator="union all">
         ${listSql}
    </foreach>
  </insert>
上面红色部分就是我们所需要拼接的
${insertSql}中包含的信息无非就是列名,表名, ${listSql}的信息就是列名和列所对应的类型
因为是批量新增,故传进来一个List ,一个表名,故而定义接口如下
public void batchInsertWithOutKey(List<DataRecord> list,String tableName);
接下来就是实现了:
第一步,通过表名查询相应的列名,类型

private List<DataRecord> getTableColumns(String tableName){
  //查询表所包含的列名,以及该列对应的类型
  Database db = DatabaseMetadataCache.getDatabase();
  Table table=db.getTable(tableName);
  //如果表不存在,直接返回空集合
  if(table==null){
   return new ArrayList<DataRecord>();
  }
  Map<String,Object> m=new HashMap<String,Object>();
  m.put("tableName", tableName);
  //去数据库中查询
  List<DataRecord> list=this.getDao().queryForDataSet("com.mip.biz.syn.data.batch.demo.getTableColumn", m).getResults();
  return list;
}
相应的mapper文件中的sql如下
  <select id="getTableColumn" parameterType="map" resultType="datarecord">
     select column_name,data_type from user_tab_columns where table_name =#{tableName}
  </select>
第二步将列名与类型放入一个map中
  List<DataRecord> columns=this.getTableColumns(tableName);
  if(columns.size()==0){
   return;
  }
  Map<String,String> columnMap=new HashMap<String,String>();
  for(DataRecord dr:columns){
   columnMap.put(dr.getString("COLUMN_NAME"), changeColumnType(dr.getString("DATA_TYPE")));
  }
第三步就开始拼接字符串了
//现将DataRecord转换成Map
  List<Map> back=new ArrayList<Map>();
  for(DataRecord d:list){
   back.add(d);
  }
//如果back的个数大于0,获取第一个Map,因为List中的Map数据都是一个样
Map dm=back.get(0);
//dm中的key可以说是列名,value是相应的值,与我们查询到的列名可以对应起来,从而获取到其对应的类型,关键代码如下:
  for(String colbjectKeys){
   insertSql.append(""+col+",");
   listSql.append("#{obj."+col+",jdbcType="+columnMap.get(col)+"}"+",");
  }
//但是从数据库中查询到类型可能与我们listSql中所需要的类型不匹配,故而需要稍微转换一下:
private String changeColumnType(String dataType){
  if("NUMBER".equals(dataType)){
   return "DOUBLE";
  }else if("VARCHAR2".equals(dataType)){
   return "VARCHAR";
  }else if(dataType.startsWith("TIMESTAMP")){
   return "TIMESTAMP";
  }else{
   return dataType;
  }
}
这个时候语句是拼接好了。怎么传入到mapper中又是一个问题,因为我们原先的是只传List,网上查询了一下foreach

foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach元素的属性主要有 item,index,collection,open,separator,close。item表示集合中每一个元素进行迭代时的别名,index指定一个名字,用于表示在迭代过程中,每次迭代到的位置,open表示该语句以什么开始,separator表示在每次进行迭代之间以什么符号作为分隔符,close表示以什么结束,在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:
1.     如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
2.     如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
3.     如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在breast里面也是会把它封装成一个Map的,map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key
解决了,将数据封装成map
Map<String,Object> batchMap=new HashMap<String,Object>();
  batchMap.put("insertSql", insertSql.toString());
  batchMap.put("listSql", listSql.toString());
  batchMap.put("list", back);
  this.getDao().insertByStatement("com.mip.biz.syn.data.batch.demo.batchDemo", batchMap);
ok,基本上到这里就完了,以下是全部实现代码
private List<DataRecord> getTableColumns(String tableName){
  //查询表所包含的列名,以及该列对应的类型
   Map<String,Object> m=new HashMap<String,Object>();
  m.put("tableName", tableName);
  //去数据库中查询
  List<DataRecord> list=this.getDao().queryForDataSet("com.mip.biz.syn.data.batch.demo.getTableColumn", m).getResults();
  return list;
}
private String changeColumnType(String dataType){
  if("NUMBER".equals(dataType)){
   return "DOUBLE";
  }else if("VARCHAR2".equals(dataType)){
   return "VARCHAR";
  }else if(dataType.startsWith("TIMESTAMP")){
   return "TIMESTAMP";
  }else{
   return dataType;
  }
}
@Override
public void batchInsertWithOutKey(List<DataRecord> list,String tableName) {
  //如果无数据,不需要执行插入了
  if(list.size()<=0){
   return ;
  }
  //获取表的列名以及类型
  List<DataRecord> columns=this.getTableColumns(tableName);
  if(columns.size()==0){
   return;
  }
  Map<String,String> columnMap=new HashMap<String,String>();
  for(DataRecord dr:columns){
   columnMap.put(dr.getString("COLUMN_NAME"), changeColumnType(dr.getString("DATA_TYPE")));
  }
  List<Map> back=new ArrayList<Map>();
  for(DataRecord d:list){
   back.add(d);
  }
  Map dm=back.get(0);
  Set<String> objectKeys = dm.keySet();
  StringBuffer insertSql=new StringBuffer();
  insertSql.append("insert into "+tableName+"(");
  StringBuffer listSql=new StringBuffer();
  listSql.append(" select ");
  for(String col:bjectKeys){
   insertSql.append(""+col+",");
   listSql.append("#{obj."+col+",jdbcType="+columnMap.get(col)+"}"+",");
  }
  // 删除最后的半角逗号
  if (insertSql.length() > 0) {
   insertSql.deleteCharAt(insertSql.length() - 1);
  }
  if (listSql.length() > 0) {
   listSql.deleteCharAt(listSql.length() - 1);
  }
  insertSql.append(")");
  listSql.append(" from dual");
  Map<String,Object> batchMap=new HashMap<String,Object>();
  batchMap.put("insertSql", insertSql.toString());
  batchMap.put("listSql", listSql.toString());
  batchMap.put("list", back);
  this.getDao().insertByStatement("com.mip.biz.syn.data.batch.demo.batchDemo", batchMap);
}
mapper配置文件内容如下
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
    PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
    "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
   <mapper namespace="com.mip.biz.syn.data.batch.demo">
   <select id="getTableColumn" parameterType="map" resultType="datarecord">
     select column_name,data_type from user_tab_columns where table_name =#{tableName}
   </select>
   
    <insert id="batchDemo" parameterType="List">
    <![CDATA[
     ${insertSql}
    ]]>
     <foreach collection="list" item="obj" index="index" separator="union all">
    ${listSql}
     </foreach>
    </insert>
</mapper>

至于有主键的批量插入下次再说
原创粉丝点击