不指定insert into 字段入全表

一次业务场景需要,得把一个数据库中的数据导入另一个数据库,2个数据库类型不同,当时采用的方式为:

        把数据库A的表中的数据导出成dat文件(这个数据库导出的文件就是dat文件),一行一条记录,字段顺序按照建表字段顺序,各个字段中间用欧元符号分隔,在数据库B中建表,表结构与数据库中的表结构完全一致,最后再增加一个ids自增字段。dat文件存放在文件服务器指定目录,程序通过ssh连接服务器,并获取dat文件流来获取文件,一行一行读取文件内容然后批量入B库。文件名称中的编码字符可以确定这个文件入哪个表。持久层框架用的是mybatis。

        总共十几+个表,表的字段也都很多,既然文件格式有约定,那入B库时的sql不计划指定字段插入,太多太麻烦。

#指定字段插入数据
insert into tableB (field1,field2) values (#{i1},#{i2});
#不指定字段插入数据,需要按顺序给出全部字段的值
insert into tableB values (#{i1} ... );

最终处理流程是:

1.通过BufferedReader的readLine()方法一行一行读数据;

2.将读出的数据按照字段分隔符(此处为€)分隔成字符串数组,再将字符串数组转成List line,再用ArrayList 包装一下;

List lineList=new ArrayList(Arrays.asList(line.split(dataFileSeparator)));

那么这个lineList里的数据就为一行数据的各个字段值。

为什么要用ArrayList再包装下呢?因为Arrays.asList()返回的对象是 Arrays的内部类,继承了AbstractList,AbstractList中的set, add, remove 抽象方法中都是直接throw new UnsupportedOperationException(); 但内部类 ArrayList 并未重写父类的 add,remove方法,所以我们不能再对存放各个字段值的List不能增加删除,如果不涉及增加其他字段值或删除字段值的操作,可以直接使用,不用包装。对Arrays.asList 再包装一层,生成的List对象就是我们熟知的 ArrayList了。

 3.在循环外层定义一个ArrayList datas,用来存放一行一行数据的List lineList;

4.将datas作为参数传给mapper中的入库方法,xml这么写的:

<insert id="insertModelData">
    insert into ${tableName} values
        <foreach collection="list" item="line" separator=",">
            (
            <foreach collection="line" item="item" separator=",">
                #{item}
            </foreach>
            )
        </foreach>
</insert>

表名根据文件名称映射,第一层foreach取出一行一行的所有数据,第二层foreach取出每行的各个字段。

就这样,不用写一大堆sql,一个方法就可以满足此任务的所有表的数据入库操作。但是要注意${}、#{}的区别,有需要要额外处理${}字段。

此任务中其他需要注意的问题

1.BufferedReader的readLine()读出的字符串中不包含结尾的"\r","\n"

2.BufferedReader的readLine()相关源码如下:

public String readLine() throws IOException {
    return readLine(false);
}
String readLine(boolean ignoreLF) throws IOException {
        StringBuffer s = null;
        int startChar;

        synchronized (lock) {
            ensureOpen();
            boolean omitLF = ignoreLF || skipLF;

        bufferLoop:
            for (;;) {

                if (nextChar >= nChars)
                    fill();
                if (nextChar >= nChars) { /* EOF */
                    if (s != null && s.length() > 0)
                        return s.toString();
                    else
                        return null;
                }
                boolean eol = false;
                char c = 0;
                int i;

                /* Skip a leftover '\n', if necessary */
                if (omitLF && (cb[nextChar] == '\n'))
                    nextChar++;
                skipLF = false;
                omitLF = false;

            charLoop:
                for (i = nextChar; i < nChars; i++) {
                    c = cb[i];
                    if ((c == '\n') || (c == '\r')) {
                        eol = true;
                        break charLoop;
                    }
                }

                startChar = nextChar;
                nextChar = i;

                if (eol) {
                    String str;
                    if (s == null) {
                        str = new String(cb, startChar, i - startChar);
                    } else {
                        s.append(cb, startChar, i - startChar);
                        str = s.toString();
                    }
                    nextChar++;
                    if (c == '\r') {
                        skipLF = true;
                    }
                    return str;
                }

                if (s == null)
                    s = new StringBuffer(defaultExpectedLineLength);
                s.append(cb, startChar, i - startChar);
            }
        }
    }

可以看出该方法可以设置跳过LF,但是该方法访问权限为default,不支持继承该类重写方法,而且其他方法也不支持我们设置修改变量,所以我们没办法控制readLine()是读到\r 换行还是 \n换行还是\r\n换行,我们没法指定特定换行符来让它读一行,所以文件中有^M时需要留意,有可能它读了半行就返回了。

在windows平台下,换行符是\r\n,而在linux下是\n,这多出来的\r被vim解释成了^M。

 windowslinuxMacBook
换行符\r\n\n\r
ASCII0x0d0a0x0a0x0d

  其中:

  • "\r"在ASCII中表示“换行(LF)”
  • "\n"在ASCII中表示“回车(CR)”
访问控制修饰符
 privatedefaultprotectedpublic
同一类中的成员
同一包中其他类 
不同包中的子类  
不同包中非子类   

笔者当时就遇到^M这个问题,返回半行,入库时mysql报字段不匹配的错误,当然了,少字段嘛。当时时间紧而且是临时需求 ,过一段时间就不用了,所以没有通过程序解决,采用的方式是替换文件中的^M符号。通过程序的话貌似得用read方法,这个就有点复杂了,到指定的换行符才停,逻辑有点小复杂,没时间呀~这里mark一下,有空研究,也欢迎有经验的朋友分享心得。

以下内容来自:Linux下去掉^M的4种方法

第一种方法:

cat -A filename 就可以看到windows下的断元字符 ^M

要去除他,最简单用下面的命令:

dos2unix filename

第二种方法:

1

2

sed -i ‘s/^M//g' filename

#注意:^M的输入方式是 Ctrl + v ,然后Ctrl + M

第三种方法: 

1

2

3

#vi filename

:1,$ s/^M//g

^M 输入方法: ctrl+V ,ctrl+M

第四种方法:

1

2

#cat filename |tr -d ‘/r' > newfile

#^M 可用 /r 代替

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值