1.jdbc调用oralce自定义的数组类型并批量更新数据

  首先在这里说一下目的:公司要求用jdbc插入weka分析数据后的模型来分析某条数据对结果的影响概率,并将这条概率插入到这条数据所在的表中,刚开始是一条一条的去调用数据库存储过程,效率很低。

现在需要一次性的插入100条数据或者更多,那么问题来了,如何用java识别oralce自定义的数组来完成这件事呢?过程是曲折的。

  一、我在数据库中定义了一个类型和一个表数组(就是数组长度无限制的数组)。

  1.定义了一个类型数据

  在这一部分很重要,为了跟数据库字段进行匹配,刚开始这三个属性的类型都是varchar2,对应表中要插入的字段和唯一标识字段,但是数据就是插入不了。下文会说这个问题。

create or replace TYPE ana_type1 AS OBJECT (
result0 nvarchar2(300),
result1 nvarchar2(300),
uuid nvarchar2(10)
);

   2.定义一个表数组。

create or replace TYPE array_ana_type1 AS TABLE OF ana_type1;

  二、预先写好存储过程

create or replace PROCEDURE OPS_RESULT_ANALYSIS(vv_array array_ana_type1,tableName varchar2) 
AS 
  up_sql varchar2(1000):='';
--  type v_type is record(result0 varchar2(50),result1 varchar2(50),uuid varchar2(50));
--  type v_array is table of v_type;
--  vv_array v_array;
BEGIN
--  vv_array:=v_array();
--  vv_array.extend;   
--  vv_array(1).result0:='0.1';
--  vv_array(1).result1:='0.2';
--  vv_array(1).uuid:='15646556';
  
  --循环数组,更改数据库数据
    delete update_sql;
    for i in vv_array.FIRST..vv_array.LAST  loop
      up_sql:='update '||tableName||' set latent='''||vv_array(i).result0||''','||'latent2='''||vv_array(i).result1||''' where card_no='''||vv_array(i).uuid||''' and 1=1';
      --dbms_output.put_line(up_sql);
      insert into update_sql values(up_sql);
      execute immediate up_sql;
      if tableName='OPR_RESULT_FR5' then
        up_sql:='update OPR_RESULT_FR5 set latent_fr='''||vv_array(i).result0||''','||'latent_fr2='''||vv_array(i).result1||''' where card_no='''||vv_array(i).uuid||''' and 1=1';
        --dbms_output.put_line(up_sql);
        execute immediate up_sql;
      end if;
    end loop;
    commit;
END;

  注释掉的部分,是我刚开始时测试up _sql拼接的是否正确,自己定义一个数组,给数组赋空间,然后复制,打印sql的步骤。

  sql拼接无误之后,就把完整的存储过程写好,以便java调用,在java调用的时候,出现了存储过程执行成功,但数据没有更新的问题。所以在此我就创建了一个只有一个字段varchar2的日志表用来记录java调用存储过程时,我拼接的sql是否正确,还可以检查传过来的数据是否正确(为空或者其他什么的,作用很大)。

  三、写好java程序

  在java部分我做的工作是:

  1.定时器调用线程。

  2.线程调用分析数据库数据的方法(是weka数据分析的工作,可以不用管它),数据库查到的每条数据产生两个概率,一个是事件的正概率,一个是事件的负概率,然后我们还需要每条数据的唯一标识,

  这三个分别对应的变量名是result0,result1,uuid。

  3.拿到这写数据后,用jdbc调用存储过程,传入数组变量,批量更新数据。(重要)

  我们一步一步贴上代码!!

 

  A、定时器部分:

package com.cmcc.task;

import java.io.File;

import org.apache.log4j.Logger;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.cmcc.dataAnalysis.CommonAnalysis;
import com.cmcc.thread.ModelTestThred;
/**
 * 数据分析定时器
 * 复诊、非复诊、晶体等定时分析
 * @author XWS
 *
 */
@Component
public class DataAnalysisTask {
    
    private   Logger LOG = Logger.getLogger(DataAnalysisTask.class);
    
    private  String q1="SELECT * from (SELECT CARD_NO,SEX_CODE, PAYKIND_CODE,PACT_CODE,PACT_NAME,REGLEVL_CODE,REGLEVL_NAME,DEPT_CODE,DEPT_NAME,DOCT_CODE,DOCT_NAME,YNFR,YNSEE,IN_SOURCE,ADDRESS_CODE,ADDRESS_NAME,AGE,DIAGNOSE,PATIENT_NO FROM OPR_RESULT fo where fo.LATENT is NULL order by fo.reg_date desc) where rownum<=100";
    private  String tableName1="OPR_RESULT";
    private  String q2="SELECT * from (SELECT fo.* FROM OPR_RESULT_FR5 fo where fo.LATENT_FR is NULL order by fo.reg_date desc) where rownum<=100 and drug_percent is not null" ;//"SELECT * FROM OPR_RESULT_FR5"; where fo.LATENT_FR is NULL
    private  String tableName2="OPR_RESULT_FR5";
    private  String [] model1= //删除不需要的属性集
        {
            "CARD_NO",
         "ADDRESS_CODE",
         "DOCT_CODE",
         "PACT_CODE",
         "REGLEVL_CODE",
         "DEPT_CODE"
        };
    private  String [] model2= //删除不需要的属性集
    {    "CLINIC_CODE",
        "CARD_NO",
         "ADDRESS_CODE",
         "DOCT_CODE",
         "PACT_CODE",
         "REGLEVL_CODE",
         "DEPT_CODE",
        "TIMES",
        "LABEL2",//是否复诊预测结果
        "LATENT_FR",//是否复诊预测结果
        "LATENT_FR2"
    };
    
    @Scheduled(cron = "0 34 10-11 * * ?")
    public void job1(){
        
        LOG.info("job1的执行时间:0 0/10 0-1 * * ?");
        
        String querySql=q1;
        String[] deletModel=model1;
        //String modelUrl="../model/randomForest.model";
        String modelUrl = "../webapps/SJFX/model/randomForest.model";
        //String insertsql="{call ops_result_update3(?,?,?)}";
        String insertsql="{CALL OPS_RESULT_ANALYSIS(?,?)}";
        ModelTestThred modelTest = new ModelTestThred(querySql,deletModel,modelUrl,insertsql,tableName1);
        new Thread(modelTest).start();
    }  
    
    @Scheduled(cron = "0 0/10 1-2 * * ?")
    public void job2(){  
        LOG.info("job2的执行时间:0 0/10 1-2 * * ?");
        String querySql=q2;
        String[] deletModel=model2;
        String modelUrl="../webapps/SJFX/model/randomForestFR.model";
        String insertsql="{call ops_result_update2(?,?,?)}";
        ModelTestThred modelTest = new ModelTestThred(querySql,deletModel,modelUrl,insertsql,tableName2);
        
        new Thread(modelTest).start();
    }
}

 

 

 



B、线程部分

package com.cmcc.thread;

import org.apache.log4j.Logger;

import com.cmcc.dataAnalysis.CommonAnalysis;

public class ModelTestThred implements Runnable{

    private String querySql;
    private String[] deletModel;
    private String modelUrl;
    private String insertsql;
    private String tableName;
    
    private   Logger LOG = Logger.getLogger(ModelTestThred.class);
    
    public ModelTestThred(String querySql, String[] deletModel, String modelUrl,
            String insertsql,String tableName) {
        this.querySql=querySql;
        this.deletModel=deletModel;
        this.modelUrl=modelUrl;
        this.insertsql=insertsql;
        this.tableName=tableName;
    }
    
    
    public ModelTestThred() {
    }


    @Override
    public void run() {
        LOG.info("线程"+Thread.currentThread().getName()+"开始分析"+modelUrl+"的数据");
        CommonAnalysis.DataTestingFunction(querySql,deletModel,modelUrl,insertsql,tableName);
        LOG.info("end analysis "+modelUrl+" at"+System.currentTimeMillis());
    }


}

 


C、分析概率部分

package com.cmcc.dataAnalysis;


import org.apache.log4j.Logger;

import weka.classifiers.Classifier;
import weka.core.Instances;
import weka.core.converters.DatabaseLoader;



/**
 * OPR_RESULT 和 OPR_RESULT_FR5两个表的数据的分析方法
 * 
 * @author XWS
 * 
 */

public class CommonAnalysis {
    public static   Logger LOG = Logger.getLogger(CommonAnalysis.class);
    
    public static void DataTestingFunction(
            String querySql,
            String[] deletModel, 
            String modelUrl, 
            String sql,
            String tableName) {// 预测住院结果
        Classifier m_classifier;
        try {
            LOG.info("start testing at" + System.currentTimeMillis());
            DatabaseLoader atf = DatabaseLoaderUtil.JDBCQuery(querySql);
            Instances instancesTest = atf.getDataSet(); // 读入训练文件
            int dataSize = instancesTest.numInstances();
            LOG.info("process data :" + dataSize);
            
            String [] uuid = new String[dataSize];
            for (int i = 0; i < dataSize; i++)// 前100个测试数据,记录卡号
            {
                uuid[i] = instancesTest.instance(i).stringValue(0);
            }
            
            DataFilterUtil.delAttr(deletModel, instancesTest);
            instancesTest.setClassIndex(instancesTest.numAttributes() - 1);
            m_classifier = (Classifier) weka.core.SerializationHelper
                    .read(modelUrl);
            String [] result0 = new String[dataSize];
            String [] result1 = new String[dataSize];
            
            for (int i = 0; i < dataSize; i++)// 测试分类结果
            {
                double[] result =null;
                try {
                    result = m_classifier
                            .distributionForInstance(instancesTest.instance(i));
                } catch (Exception e) {
                    System.out.println("分析概率出错!");
                    e.printStackTrace();
                }// 输出分类结果result[0]和result[1],其中result[0]是住院概率,result[1]是不住院概率
                /*String[] result_o = new String[result.length];
                for (int j = 0; j < result.length; j++) {
                    if (result[j] < 0.001) {
                        result_o[j] = 0 + "";
                    } else {
                        result_o[j] = result[j] + "";
                    }
                }*/
                result0[i]=result[0]<0.001?"0":(result[0]+"0");
                result1[i]=result[1]<0.001?"0":(result[1]+"0");
                //InsertResultUtil.insertResult(result_o, card_no[i], sql);// 前100个测试数据,根据其卡号,将结果插入数据库
            }
            LOG.info("准备插入"+modelUrl+"模型分析的数据");
            for(int i=0; i<dataSize;i++){
                LOG.info("result0: "+result0[i]+"\tresult1: "+result1[i]);
            }
            try {
                InsertResultUtil.insertAnalysisResult(result0, result1, uuid, tableName,sql);
                //dataAnalysisDao.insertAnalysisResult(result0, result1, uuid, tableName, false);
            } catch (Exception e) {
                LOG.error(modelUrl+"数据插入时出现错误!");
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
            LOG.error(modelUrl+"数据测试时出现错误!");
        }
    }

}

 


D、调用jdbc部分
package com.cmcc.dataAnalysis;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;

import oracle.jdbc.OracleCallableStatement;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;

import org.apache.log4j.Logger;


public class InsertResultUtil {
    
    private static   Logger LOG = Logger.getLogger(InsertResultUtil.class);
public static void insertAnalysisResult(String[]  result0, String[] result1,
            String[] uuid, String tableName, String sql) {
        Connection conn = null;
        CallableStatement csmt = null; 
        try {
            //配置数据库
            conn = JDBCUtils.getConnection();
            conn.setAutoCommit(false);
            //创建oralce类型描述对象
            StructDescriptor st = new StructDescriptor("ANA_TYPE1",conn);
            //创建oralce类型数组
            STRUCT[] sts =new STRUCT[result0.length];
            //填充自定义的数组
            for(int i=0;i<result0.length;i++){
                Object [] o = {result0[i],result1[i],uuid[i]};
                //创建类型变量
                sts[i]=new STRUCT(st,conn,o);
                System.out.println(i);
            }
            //创建数组描述对象
            ArrayDescriptor ad = ArrayDescriptor.createDescriptor("ARRAY_ANA_TYPE1", conn);
            //创建数组对象
            ARRAY ARRAY_ANA_TYPE1 = new ARRAY(ad, conn, sts); 
            csmt = conn.prepareCall(sql);  // 无参数无返回结果的存储过程
            //添加占位符
            ((OracleCallableStatement) csmt).setArray(1,ARRAY_ANA_TYPE1);
            csmt.setString(2,tableName);
            boolean falg = csmt.execute();//执行成功返回false
            System.out.println(falg);
            System.out.println(csmt.getUpdateCount());
            conn.commit();
            csmt.close();
            conn.close();
            LOG.info("分析"+tableName+"表的数据已经成功插入!");
        } catch (SQLException e) {
            e.printStackTrace();
        }        
    }
}

 

四、遇到的问题

  途中遇到很多问题,但解决起来也不费力,但是有一个问题是这样的,程序运行的时候,在调用存储过程并更新数据的时候,什么错误都没有,数据库也没有更新;

然后我请教别人,给了我把sql插入到日志表中的建议,所以我建了一个表来存每次的更新sql,发现从java传过来的数据都是空的,所以我才知道数据并没有真正的传过来。
  
  之后我又找到了网上的一些别人的例子,参照网页http://blog.csdn.net/hzw2312/article/details/8444462,里面有这么一段话:

        最后:一定要记得导入orai18n.jar否则一遇到字符串就乱码、添加不到数据!

        一些加上jar继续报错如下错误的朋友可以考虑以下解决方案:

        ERROR1:Non supported character set: oracle-character-set-852

        ERROR2:oracle/i18n/text/converter/CharacterConverterOGS.getInstance(I)Loracle/i18n/text/converter/CharacterConverter;

        以上两个错误可以采取一个方案,就是把type的数据类型改成:nVARCHAR2,

          如果坚持使用该jar的童鞋请将 nls_charset12.jar 加入到 classpath 中。在加上orai18n.jar

所以我才知道用这个oralce数组,必须要有orai18n.jar和nls_charset12.jar,我找到这两个架包添加到项目中去,但是还是在启动项目的时候就报错,查一查,有的人说是jar版本的问题,我头疼。

  突然看到以上两个错误可以采取一个方案,就是把type的数据类型改成:nVARCHAR2,我就本着鱼死网破的心态堵了一把,没想到真的成功了!!!功夫不负有心人,数据库数据成功插入。

  望有心人看了这篇文章有所收获!!
    








posted @ 2016-08-10 11:15  博智星  Views(681)  Comments(0)    收藏  举报