java批量插入数据库之写绑定变量

2023-10-12 09:21:00 浏览数 (1)

最近查到关于sql批量写入和绑定变量的问题,看到了很多好的帖子,再次进行总结

数据库在执行SQL语句时会首先解析SQL语句,解析又分为硬解析与软解析。说到硬解析和软解析,就不能不说一下Oracle对sql的处理过程。当你发出一条sql语句交付Oracle,在执行和获取结果前,Oracle对此sql将进行几个步骤的处理过程:

1、语法检查(syntax check)

  检查此sql的拼写是否语法。

2、语义检查(semantic check)

   诸如检查sql语句中的访问对象是否存在及该用户是否具备相应的权限。

3、对sql语句进行解析(prase)

   利用内部算法对sql进行解析,生成解析树及执行计划。

4、执行sql,返回结果(execute and return)。

其中,软、硬解析就发生在第三个过程里,Oracle利用内部的hash算法来取得该sql的hash值,然后在librarycache里查找是否存在该hash值。假设存在,则将此sql与cache中的进行比较(注意此处的比较哪怕是一个字母的大小写、空格个数不一致都会认为不同)。假设“相同”,就将利用已有的解析树与执行计划,而省略了优化器的相关工作。这也就是软解析的过程;如果上面的2个假设中任有一个不成立,那么优化器都将进行创建解析树、生成执行计划的动作。这个过程就叫硬解析。由此可以看出应该极力避免硬解析,尽量使用软解析。

通过http://blog.csdn.net/duanning397/article/details/7586609 我们看出两者的差距还是很大的

那么如何进行软解析,在这里主要通过绑定变量的方法

      认识绑定变量:

  绑定变量是为了减少解析,比如你有个语句这样的:

  select aaa,bbb from ccc where ddd=eee;

  如果经常通过改变eee这个谓词赋值来查询,如下:

  select aaa,bbb from ccc where ddd=fff;   select aaa,bbb from ccc where ddd=ggg;   select aaa,bbb from ccc where ddd=hhh;

  每条语句都要被数据库解析一次,这样比较浪费资源,如果把eee换成绑定变量形式,无论ddd后面是什么值,都不需要重复解析

通过

那么如何在java中实现绑定变量,主要用两种方法,一种是?号法,比较简单粗暴,适合大部分情况

具体可以参考http://blog.csdn.net/wacthamu/article/details/7798393 这里转载一些关键内容

 Java实现绑定变量的方法:

PreparedStatement pstmt = con.prepareStatement("UPDATE employees SET salay = ? WHERE id = ?"); pstmt.setBigDecimal(1, 15.00); pstmt.setInt(2, 110592); //result statmement:   UPDATE employees SET salay = 15.00 WHERE id =110592 pstmt.executeQuery();

  假设要将id从1到10000的员工的工资都更新为150.00元,不使用绑定变量,则:

sql.executeQuery("UPDATE employees SET salay = 150.00 WHERE id = 1"); sql.executeQuery("UPDATE employees SET salay = 150.00 WHERE id = 2"); sql.executeQuery("UPDATE employees SET salay = 150.00 WHERE id = 3"); sql.executeQuery("UPDATE employees SET salay = 150.00 WHERE id = 4"); .... sql.executeQuery("UPDATE employees SET salay = 150.00 WHERE id = 10000");

  使用绑定变量,则:

PreparedStatement pstmt; for (id = 1; id < 10000; id ) {    if (null == pstmt)      pstmt = con.prepareStatement("UPDATE employees SET salay = ? WHERE id = ?");    pstmt.setBigDecimal(1, 150.00);    pstmt.setInt(2, id);    pstmt.executeQuery(); }

       需要注意的是pstmt.executeQuery();是每次都打开游标,所以出国处理大数据的话,则需要在后面添加pstmt.close();

  这里可以看到通过设置的方式可以让数据库认为成一条语句,这里必须按照这种方法,如果用简单的字符串拼接的方法,数据库依然认为成两条语句具体可以参考http://www.itpub.net/thread-263389-1-1.html

那么第二种方法是什么呢,细心的网友可以看到http://blog.csdn.net/wacthamu/article/details/7798393 已经提及,即使用:x的方式,其中该文章,也给出了一样的赋值方式,

其实Oracle遵循针对PL/SQL存储过程使用占位符名称匹配的原则,而针对SQL语句则采用占位符位置匹配的原则。所以上文的:x更多的是在Oracle中的本身里面,对比两者可以发现,一种是单纯的问号,一种是用变量名,当赋值的变量是重复的时候,用后者可以避免重复书写,但后者由于是变量名,也会导致书写错误,

在近期研究中发现,在hibernate中的hql语言其实也是可以占位符名称匹配,这里给出一个完整的hql的存储例子

代码语言:javascript复制
<span style="color:#333333;">StringBuffer hql = new StringBuffer();
        hql.append("select distinct employee  from PreceptPO pre,PlanPO plan, ").append(
                "EmployeeInfoPO employee,ObjectRelationPO relation ");
        hql.append(" where pre.oid=plan.preceptID");
        hql.append(" and pre.status='").append(PreceptStatusType.STARTUP.getCode()).append("'");
        hql.append(" and plan.objectID=employee.oid ");
        hql.append(" and relation.relationObjectID=:employeeID ");
        hql.append(" and relation.preceptID=plan.preceptID ");
        hql.append(" and relation.objectID=plan.objectID ");
        hql.append(" and relation.phase=plan.phase ");
        hql.append(" and (relation.performanceRole=3 or relation.performanceRole=4 )");
        

        //员工状态
        hql.append(" and ");
        DAOHelper.buildOrHql(hql, "employee.employeeStatus",
                StaffStatusAttribute.Normal_Employee.getStatus());

        Query q = getSession().createQuery(hql.toString());
        /*if(empList.size() > 0){
        	</span><span style="color:#ff0000;"><strong>q.setParameterList("empList", empList);	</strong></span><span style="color:#333333;">
        }*/
        
        q.setLong("employeeID", appraiserID);</strong>
        objectList = q.list();
        
        List voList = new ArrayList(objectList.size());
        for (Iterator iter = objectList.iterator(); iter.hasNext();) {
            EmployeeInfoPO emp = (EmployeeInfoPO) iter.next();
            voList.add(EmployeeBasicConvert.convert(emp));
        }
    	return voList;
	}</span>

这个时候很多人会说,既然使用了createQuery,那使用createSQLQuery可不可以用占位符名称匹配,答案是可以的,下面给出了例子

代码语言:javascript复制
StringBuffer buffer = new StringBuffer();
/*
		buffer.append(" select count(1) as num from TB_PMG_OBJECTRELATION orn,TB_PMG_PLAN pl ")
		      .append(" left join TB_PMG_EVALUATESCORE on TB_PMG_EVALUATESCORE.c_planid = pl.c_oid_plan ")
	          .append(" and TB_PMG_EVALUATESCORE.c_status in (2,3,5,7) ")
	          .append(" where pl.c_oid_plan=").append(planID)
	          .append(" and pl.c_objectid = orn.c_objectid ")
	          .append(" and orn.c_preceptid = pl.c_preceptid and orn.c_phase = pl.c_phase ")
	          .append(" and orn.c_level = '2' and orn.c_businessrole='1' ")
	          .append("  and TB_PMG_EVALUATESCORE.C_OID_EVALUATESCORE is not null");
*/
		buffer.append(" SELECT COUNT(1) AS NUM FROM TB_PMG_OBJECTRELATION ORN  ")
	      .append(" LEFT JOIN  TB_PMG_EVALUATESCORE e on e.C_STATUS IN (2,3,5,7)")
	      .append(" AND ORN.C_PRECEPTID = e.c_preceptID ")
	      .append(" AND ORN.C_PHASE = e.c_phase ")
	      .append(" AND ORN.C_objectID = e.c_objectID ")
	      .append(" WHERE  ")
	      .append(" ORN.C_OBJECTID =:objectID  ")
	      .append(" AND ORN.C_PRECEPTID = :preceptID AND ORN.C_PHASE = :phase ")
	      .append(" AND ORN.C_LEVEL = '2' AND ORN.C_BUSINESSROLE='1' ")
	      .append(" AND e.C_OID_EVALUATESCORE IS NOT NULL ");

		SQLQuery q= loadSession().createSQLQuery(buffer.toString());
		q.setParameter("preceptID", preceptID);
		q.setParameter("phase", phase);
		q.setParameter("objectID", objectID);</strong>
		q.addScalar("num", Hibernate.LONG);
		List list = q.list();

其实在大型工程中,占位符名称匹配用的很多,但如果就是用单纯的sql语句怎么实现呢?,下文给出了方式http://blog.csdn.net/keyboardsun/article/details/4275594

这里需要用一个自定义函数,进行拼接赋值,这里面还对不用类型的内容进行转换,比如时间,数字等比较复杂的转换,所以这个自定义函数可以作为一个工具类

很感谢以上所有的博主,给我的帮助,在这里也专程感谢

0 人点赞