jdbc操作根据bean类自动组装sql,天啦,我感觉我实现了hibernate

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 场景:需要将从ODPS数仓中计算得到的大额可疑交易信息导入到业务系统的mysql中供业务系统审核。最简单的方式是用阿里云的组件自动进行数据同步了。但是本系统是开放是为了产品化,要保证不同环境的可移植性,同时同步的表也就6个表,那么就利用现有的基于jdbc的规则引擎工程来自己实现数据的同步。

场景:需要将从ODPS数仓中计算得到的大额可疑交易信息导入到业务系统的mysql中供业务系统审核。最简单的方式是用阿里云的组件自动进行数据同步了。但是本系统是开放是为了产品化,要保证不同环境的可移植性,同时同步的表也就6个表,那么就利用现有的基于jdbc的规则引擎工程来自己实现数据的同步。

完整的工程代码可以参考我的github  https://github.com/intsmaze/SqlAdapter

 JDBC手动将一个库的数据导入到另一个数据库中,如何避免人工映射操作,提高开发效率

讨厌的方式

查询数据,将结果映射到javabean对象中
ps = conn.prepareStatement(select id,name,age from intsmaze);
rs = ps.executeQuery();
Intsmaze intsmaze = null;
while (rs.next()) {
    intsmaze = new Intsmaze();
    intsmaze.setId(rs.getInt(1));
    intsmaze.setName(rs.getString(2));
    intsmaze.setAge(rs.getInt(3));
}
添加数据,将javabean对象字段映射到对应的表列
String sql = "insert into intsmaze(id,name,age)  values (?,?,?)";
ps = conn.prepareStatement(sql);
ps.setInt(1, intsmaze.getId());
ps.setString(2, intsmaze.getName());
ps.setInt(3, intsmaze.getAge());
ps.executeUpdate();

使用JDBC大家都会使用上面的方式进行开发,但是如果我们的表的字段有50个,而且查询不能使用 select * from 必须指定列名呢?添加数据不能使用insert into intsmaze values()必须指定插入的列名呢?你干脆杀了我吧。

50个字段你要做2次字段列名映射,稍有不慎就会将字段列名映射到错误的位置,导致最后数据错误,最可怕的是,还要编写sql语句,如果后面有新增或删除列名,那么你又要去看一眼映射关系,看看是否影响到。这就是一种费力却没有技术含量的事情,而且还很容易出错。下面就是我们要做的各种映射,你真的很考验我的眼神。

result.getObjectByName("FieldName")

javabean.setFieldValue()

PreparedStatement.setString(number,FieldVale)

根据javabean自动生成insert,select语句,完成字段列名映射

 

根据javabean自动生成insert,select语句,完成字段列名映射

 

当初开发时,一看到这么多字段映射我烦躁不安,然后花了半天用反射把代码重新编写了下,后面有新的表要进行同步时,用一个工具类生成javabean的java文件,然后直接就在下面模板代码中替换javabean类就完成了数据同步,整个操作10分钟搞定,是不是很爽。

当然你可以引入orm框架,但是除了hibernate框架,mybatis框架虽然免去了select和insert的映射,但是还是要编写前缀列名,而且我就一个小工程,我再引入ORM框架,麻不麻烦啊,有这时间还不如自己写一写。

public class ModelServer extends ApplicationServer {

    private static final Logger logger = LoggerFactory.getLogger(ExportBlockCustomerServer.class);
    private final static String SQL = "get_customer_infor";
    private MysqlService<TestGroup> mysqlService = new MysqlService<TestGroup>();
    @Override
    public String[] getPaths() {
        return new String[] { "com/hand/service/exe/blocktrade/blocktrade.xml" };
    }

    private int date = 1;
    @Override
    public void addOptions(Options options) {
        options.addOption("date", true, "天数");
    }
    @Override
    public void setupOptionValue(CommandLine cmd) {
        date = Integer.parseInt(cmd.getOptionValue("date", "1"));
        logger.debug("date is {}", date);
    }

    public void service() throws Exception {
        mysqlService.setMysqlDao(this.getMysqlDao());
        AmlException exception = null;
        for (int i = 1; i <= date; i++) {

            String exeSql = (String) this.getSqlMap().get(SQL);
            Result result = this.getSqlAdapter().select(this.getDao(),"SELECT * from test_group");//向odps数据仓库查询数据,并导入到mysql中

            String[] names = FilesNameUtils.getFiledName(new TestGroup());//得到这个bean类的所有字段名称,要保证bean类的字段名称和数据库表的列名一致
            String insertSql = SqlUtils.getInsertSql("test_group", names);//组装成insert into test_group (id,cny,d,party_id,age) values (?,?,?,?,?)语句
            
            List<TestGroup> list = new ArrayList<TestGroup>(100);
            int number = 0;
            while (result.hasNext()) {
                result.next();
                TestGroup br = (TestGroup) tableToBean(result, i,names);//将odps查询的数据反射到TestGroup类中,不用反射见重载函数
                list.add(br);
                number++;
                if (number % 100 == 0) {
                    try {
                        mysqlService.insert(insertSql, list, names);
                    } catch (Exception e) {
                        exception = new AmlException(e);
                    } finally {
                        list.clear();
                    }
                }
            }
            try {
                mysqlService.insert(insertSql, list, names);
                logger.info("insert data number is {}", number);
            } catch (Exception e) {
                logger.info("insert data number is {}", number);
                exception = new AmlException(e);
            } finally {
                result.close();
            }

        }
        if (exception != null) {
            throw exception;
        }
    }

    public static void main(String[] args) throws Exception {
        ModelServer applicationServer = new ModelServer();
        applicationServer.run(args);
        logger.info("execute sucess......");
        System.exit(0);
    }

    private Object tableToBean(Result result, int i, String[] names) throws Exception {
        Class clazz = TestGroup.class;
        TestGroup testGroup = (TestGroup) clazz.newInstance();
        for (int j = 0; j < names.length; j++) {
            Object object = result.getObjectByName(names[j]);
            if (object instanceof  String) {
                 Field f = clazz.getDeclaredField(names[j]);
                 f.setAccessible(true);
                 f.set(testGroup, object);
            } else if (object instanceof  Date) {
                 Field f = clazz.getDeclaredField(names[j]);
                 f.setAccessible(true);
                 f.set(testGroup, new java.sql.Date(((Date)object).getTime()));
            }
            else if (object instanceof Long) {
                 Field f = clazz.getDeclaredField(names[j]);
                 f.setAccessible(true);
                 f.set(testGroup, object);
            } 
        }
        return testGroup;
    }

    private Bigamountreport tableToBean(Result result, int i)
            throws SQLException {
        Bigamountreport bigamountreport = new Bigamountreport();
        bigamountreport.setSeqno((String) result.getObjectByName("aml_id"));
        bigamountreport.setCustomerId((String) result
                .getObjectByName("party_id"));
        ......疯狂的set操作return bigamountreport;
    }

}

获得传入对象的字段名称的字符串数据,为了拼接sql使用

    public static String[] getFiledName(Object o) {
        Field[] fields = o.getClass().getDeclaredFields();
        String[] fieldNames = new String[fields.length];
        for (int i = 0; i < fields.length; i++) {
            fieldNames[i] = fields[i].getName();
        }
        return fieldNames;
    }

拼接insert的sql语句

public static String getInsertSql(String tableName ,String[] names)
    {
        String insertSql = StringUtils.join("insert into ",tableName ," (#{field_name}) values (#{field_value})");
        String fieldName="";
        String fieldValue="";
        for(int j = 0; j < names.length; j++)
        {
            if(j==names.length-1)
            {
                fieldName=StringUtils.join(fieldName,names[j]);
                fieldValue=StringUtils.join(fieldValue,"?");
            }
            else
            {
                fieldName=StringUtils.join(fieldName,names[j],",");
                fieldValue=StringUtils.join(fieldValue,"?",",");
            }
        }
        insertSql=insertSql.replace("#{field_name}", fieldName).replace("#{field_value}", fieldValue);
        logger.debug("the insert sql is :{}",insertSql);
        return insertSql;
    }

 

最重要的是assembleBeantoPS方法,用于根据映射字段列名

public class MysqlService<T> {
	private static final Logger logger = LoggerFactory.getLogger(MysqlService.class);

	private MysqlDao mysqlDao;

	public void assembleBeantoPS(PreparedStatement ps, int number,
			String FileName, Object bean) throws Exception {
		Type fileType = FilesNameUtils.getFieldType(FileName, bean);//根据属性名称返回字段类型
		if (fileType == String.class) {
			ps.setString(number + 1,
					(String) FilesNameUtils.getFieldValueByName(FileName, bean));
		}
		else if ("long".equals(fileType.toString()+"")) {
			ps.setLong(number + 1,(Long) FilesNameUtils.getFieldValueByName(FileName, bean));
		}
		else if (fileType == Long.class) {
			ps.setLong(number + 1,(Long) FilesNameUtils.getFieldValueByName(FileName, bean));
		}
		else if (fileType == Date.class) {
			ps.setDate(number + 1,new java.sql.Date(((Date)FilesNameUtils.getFieldValueByName(FileName, bean)).getTime()));
		}
	}

	/**
	 * @author:YangLiu
	 * @date:2017年12月25日 下午3:52:20
	 * @describe:
	 */
	public void insert(String sql, List<T> list,
			String[] names) throws Exception {
		boolean iserror=false;
		PreparedStatement ps = null;
		try {
			ps = mysqlDao.getConnection().prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
			for (int i = 0; i < list.size(); i++) {
				T bigamount = list.get(i);
				try{
					for (int j = 0; j < names.length; j++) {
						assembleBeantoPS(ps, j, names[j], bigamount);
					}
					ps.executeUpdate();
				}catch (Exception e) {
					iserror=true;
					logger.error("插入数据发生错误, occur {} ", e);
					logger.error("异常数据 {} ", bigamount);
				}
			}
		} catch (Exception e) {
			mysqlDao.getInstance().free(null, ps, mysqlDao.getConnection());
			logger.error("the sql: {} occur {} ", sql, e);
			throw new AmlException("mysql建立连接时发生异常");
		} finally {
			mysqlDao.getInstance().free(null, ps);
			if(iserror)
			{
				throw new AmlException("向mysql中导入数据时发生异常");
			}
		}
	}
	
	/**
	 * @deprecated
	 * @author:YangLiu
	 * @date:2017年12月25日 下午3:52:20
	 * @describe:SB写法
	 */
	public boolean insertBatchBigamountrecord(String sql,
			List<Bigamountrecord> list) throws Exception {
		PreparedStatement ps = null;
		try {

   		    // dseqno,transOrgId,policyNo,antPolicyNo,periodPrem,transactionAmountCny,transactionAmountUsd
			ps = mysqlDao.getConnection().prepareStatement(sql,
					Statement.RETURN_GENERATED_KEYS);
			for (int i = 0; i < list.size(); i++) {
				Bigamountrecord bigamountrecord = list.get(i);
				int j = 1;
				ps.setString(j++, bigamountrecord.getDseqno());
				ps.setString(j++, bigamountrecord.getTransOrgId());
				ps.setString(j++, bigamountrecord.getPolicyNo());
				......疯狂的set操作
				ps.addBatch();
			}
			ps.executeBatch();
		} catch (SQLException e) {
			mysqlDao.getInstance().free(null, ps, mysqlDao.getConnection());
			return false;
		} finally {
			mysqlDao.getInstance().free(null, ps);
		}
		return true;
	}
}

 

作者: intsmaze(刘洋)
老铁,你的--->推荐,--->关注,--->评论--->是我继续写作的动力。
微信公众号号:Apache技术研究院
由于博主能力有限,文中可能存在描述不正确,欢迎指正、补充!
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
SQL Java 数据库连接
[SQL]SQL注入与SQL执行过程(基于JDBC)
本文介绍了SQL注入的概念及其危害,通过示例说明了恶意输入如何导致SQL语句异常执行。同时,详细解释了SQL语句的执行过程,并提出了使用PreparedStatement来防止SQL注入的方法。
45 1
|
2月前
|
SQL 分布式计算 Java
大数据-96 Spark 集群 SparkSQL Scala编写SQL操作SparkSQL的数据源:JSON、CSV、JDBC、Hive
大数据-96 Spark 集群 SparkSQL Scala编写SQL操作SparkSQL的数据源:JSON、CSV、JDBC、Hive
66 0
|
2月前
|
SQL 分布式计算 关系型数据库
Hadoop-24 Sqoop迁移 MySQL到Hive 与 Hive到MySQL SQL生成数据 HDFS集群 Sqoop import jdbc ETL MapReduce
Hadoop-24 Sqoop迁移 MySQL到Hive 与 Hive到MySQL SQL生成数据 HDFS集群 Sqoop import jdbc ETL MapReduce
110 0
|
2月前
|
SQL 分布式计算 关系型数据库
Hadoop-23 Sqoop 数据MySQL到HDFS(部分) SQL生成数据 HDFS集群 Sqoop import jdbc ETL MapReduce
Hadoop-23 Sqoop 数据MySQL到HDFS(部分) SQL生成数据 HDFS集群 Sqoop import jdbc ETL MapReduce
51 0
|
2月前
|
SQL 分布式计算 关系型数据库
Hadoop-22 Sqoop 数据MySQL到HDFS(全量) SQL生成数据 HDFS集群 Sqoop import jdbc ETL MapReduce
Hadoop-22 Sqoop 数据MySQL到HDFS(全量) SQL生成数据 HDFS集群 Sqoop import jdbc ETL MapReduce
59 0
|
4月前
|
Java 数据库连接 数据库
告别繁琐 SQL!Hibernate 入门指南带你轻松玩转 ORM,解锁高效数据库操作新姿势
【8月更文挑战第31天】Hibernate 是一款流行的 Java 持久层框架,简化了对象关系映射(ORM)过程,使开发者能以面向对象的方式进行数据持久化操作而无需直接编写 SQL 语句。本文提供 Hibernate 入门指南,介绍核心概念及示例代码,涵盖依赖引入、配置文件设置、实体类定义、工具类构建及基本 CRUD 操作。通过学习,你将掌握使用 Hibernate 简化数据持久化的技巧,为实际项目应用打下基础。
334 0
|
4月前
|
SQL 数据库 Java
HQL vs SQL:谁将统治数据库查询的未来?揭秘Hibernate的神秘力量!
【8月更文挑战第31天】Hibernate查询语言(HQL)是一种面向对象的查询语言,它模仿了SQL的语法,但操作对象为持久化类及其属性,而非数据库表和列。HQL具有类型安全、易于维护等优点,支持面向对象的高级特性,内置大量函数,可灵活处理查询结果。下面通过示例对比HQL与SQL,展示HQL在实际应用中的优势。例如,HQL查询“从员工表中筛选年龄大于30岁的员工”只需简单地表示为 `FROM Employee e WHERE e.age &gt; 30`,而在SQL中则需明确指定表名和列名。此外,HQL在处理关联查询时也更为直观易懂。然而,对于某些复杂的数据库操作,SQL仍有其独特优势。
69 0
|
4月前
|
API Java 数据库连接
从平凡到卓越:Hibernate Criteria API 让你的数据库查询瞬间高大上,彻底告别复杂SQL!
【8月更文挑战第31天】构建复杂查询是数据库应用开发中的常见需求。Hibernate 的 Criteria API 以其强大和灵活的特点,允许开发者以面向对象的方式构建查询逻辑,同时具备 SQL 的表达力。本文将介绍 Criteria API 的基本用法并通过示例展示其实际应用。此 API 通过 API 构建查询条件而非直接编写查询语句,提高了代码的可读性和安全性。无论是简单的条件过滤还是复杂的分页和连接查询,Criteria API 均能胜任,有助于提升开发效率和应用的健壮性。
155 0
|
4月前
|
SQL Java 数据库连接