简体   繁体   English

Ebean调用存储过程并将ResultSet转换为Model

[英]Ebean calling stored procedure and converting ResultSet to Model

I'm working in report module, in order to do that I'm creating different stored procedures. 我正在报表模块中工作,为此,我正在创建不同的存储过程。 I create the procedure with in parameters and then create a class to map the row (resultSet) I think that's the best way to work arround performance and clarity.( what do you think about that? ) 我使用in参数创建过程,然后创建一个类来映射行(resultSet),我认为这是提高性能和清晰度的最佳方法。( 您对此有何看法?

I'm using play framework and ebean orm (2.7.7) 我正在使用播放框架和ebean orm(2.7.7)

I'm calling the store procedure and getting the resultSet, but I would like to use ebean in order to cast automaticly the row to model ... other option is take the row-cell and cast it in a property but I'm trying to avoid it. 我正在调用存储过程并获取resultSet, 但是我想使用ebean以便自动将行强制转换为模型 ...另一种选择是采用行单元格并将其强制转换为属性,但是我正在尝试避免它。

This is the current approach 这是目前的方法
Is this the best way to call an stored procedure? 这是调用存储过程的最佳方法吗?

    Transaction tx = Ebean.beginTransaction();
    String sql = "{CALL report(?, ?, ?, ?, ?, ?)}";

    CallableStatement callableStatement = null;

    try {

        Connection dbConnection = tx.getConnection();

        callableStatement = dbConnection.prepareCall(sql);

        callableStatement.setInt(1, 3);
        callableStatement.setInt(2, 5);
        callableStatement.setInt(3, 2013);
        callableStatement.setInt(4, 1);
        callableStatement.setInt(5, 2014);
        callableStatement.setInt(6, 5);

        ResultSet rs = callableStatement.executeQuery(sql);


        while (rs.next()) {
            //HOW TO CONVER row -> model ?
        }

        Ebean.commitTransaction();

    } catch (SQLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

I've discarded RawSQL and Query because received an error 我已丢弃RawSQL和Query,因为收到错误
RuntimeException: Error parsing sql, can not find SELECT keyword in: xxxxx RuntimeException:解析sql时出错,无法在xxxxx中找到SELECT关键字

Also I found other option... using CallableSql 我也找到了其他选择...使用CallableSql

 String sql = "{call sp_order_mod(?,?)}";

 CallableSql cs = Ebean.createCallableSql(sql);

 cs.setParameter(1, "turbo");
 cs.registerOut(2, Types.INTEGER);

 Ebean.execute(cs);

 // read the out parameter
 Integer returnValue = (Integer) cs.getObject(2);

but in this case I need to return a ResultSet not simply parameter . 但是在这种情况下, 我需要返回一个ResultSet而不是简单的parameter

I'm going to share my own solution. 我将分享我自己的解决方案。

  1. I get a class called ResultSetUtils.(you can google it some implementation) 我得到了一个名为ResultSetUtils的类。
  2. I added a generic method in order to return a typed list from resultset 我添加了一个通用方法以便从结果集中返回一个类型化列表

     public static <T> List<T> populateInList(Class<T> c, final ResultSet rs) { List<T> listTyped = new ArrayList<T>(); try { if (rs != null) { while (rs.next()) { T o = c.newInstance(); // MAGIC LINE populate(o, rs); listTyped.add(o); } rs.close(); } } catch (final Exception e) { // TODO Auto-generated catch block System.err.println(e.getMessage()); } return listTyped; } 

    This class to do the population use org.apache.commons.beanutils package BeanUtils.populate(bean, propertiesRealName); 此类使用org.apache.commons.beanutils包BeanUtils.populate(bean,propertiesRealName);来填充。

  3. Using 使用

     private static void callingProcedureTest() { Logger.debug("Init callingProcedureTest"); Transaction tx = Ebean.beginTransaction(); // String sql = "{CALL sp_report_test(3, 5, 2013, 1, 2014, 5)}"; String sql = "CALL sp_report_test(?, ?, ?, ?, ?, ?);"; try { Connection dbConnection = tx.getConnection(); CallableStatement callableStatement = dbConnection.prepareCall(sql); callableStatement.setInt(1, 3); callableStatement.setInt(2, 5); callableStatement.setInt(3, 2013); callableStatement.setInt(4, 1); callableStatement.setInt(5, 2014); callableStatement.setInt(6, 5); Logger.debug("SQL > " + sql); ResultSet rs = callableStatement.executeQuery(); Class<ReportTestResult> c = ReportTestResult.class; //************** MAGIC LINE, converting ResultSet to Model List<ReportTestResult> listResult = ResultSetUtils.populateInList(c, rs); for (ReportTestResult item : listResult) { Logger.debug("item.firstName> " + item.firstName); Logger.debug("item.lastName > " + item.lastName); Logger.debug("item.year > " + item.year); } Ebean.commitTransaction(); } catch (Exception e) { Ebean.rollbackTransaction(); // TODO Auto-generated catch block e.printStackTrace(); }finally{ Ebean.endTransaction(); } } 

Plus about architecture and implementation 加上有关架构和实施的信息

For each report I'm going to create: 我将为每个报告创建:

  • a Result class (eg ReportTestResult) Result类(例如ReportTestResult)

    • intention: represent a row of report | 目的:代表一行报告| simple DTO 简单的DTO
  • a Param class (eg ReportTestParam), 一个Param类(例如ReportTestParam),

    • intention: represent the parameters (inputs / ouputs), filters of the report 意图:代表参数(输入/输出),报告过滤器

      This class should implements 该类应实现

       public interface ReportParam { public int countParameteres(); public void setParametersInCallableStatement(CallableStatement callableStatement) throws SQLException; } 
  • a Report class (eg ReportTestReport) this class should extends ReportBase 一个Report类(例如ReportTestReport),该类应扩展ReportBase

    • intention: Knows the stored procedure's name, parameters and dto result 意图:了解存储过程的名称,参数和dto结果

       public class ReportTestReport extends ReportBase<ReportTestResult, ReportTestParam> { @Override protected String getProcedureName() { return STORED_NAME; } } 
  • many Adapters... 许多适配器...

    • Each report could displayed in different charts, In this case I'm using HighCharts. 每个报告可以显示在不同的图表中,在这种情况下,我使用的是HighCharts。 Order to arrange it, I'm creating different adapters to do that. 为了安排它,我正在创建不同的适配器来做到这一点。

      EG: 例如:
      class ReportTestHighChartsAdapter 类ReportTestHighChartsAdapter

      • intention: convert a list of ReportTestResult to series and configure different options of report (eg, title, xAxis etc) 目的:将ReportTestResult的列表转换为序列,并配置报告的不同选项(例如,标题,xAxis等)

         public OptionsHC buildColumnReportV1(){ OptionsHC optionChart = new OptionsHC(); optionChart.chart = new ChartHC("column"); this.setTitle(optionChart); optionChart.yAxis = new AxisHC(new TitleHC("Fruit eaten")); ..... return optionChart; } 
        • OptionsHC is a class that represent option obj in the HighCharts framework. OptionsHC是一个类,在HighCharts框架中代表选项obj。

        • The final step is converting OptionHC to Json and use it in JavaScript (common use of highCharts) 最后一步是将OptionHC转换为Json并在JavaScript中使用它(通常使用highCharts)

What's ReportBase? 什么是ReportBase?

ReportBase class has the strategy to implements the final called to DB, also manage the transaction ReportBase类具有实现最终调用到DB的策略,还管理事务

        public class ReportTestReport extends ReportBase<ReportTestResult, ReportTestParam> {

        ...

            protected List<TResult> execute(Class<TResult> classT) {

                List<TResult> resultDTO = null;

                CallableStatement callableStatement = null;

                Logger.debug("Running procedure> " + this.getProcedureName());

                Transaction tx = Ebean.beginTransaction();

                String sql = ProcedureBuilder.build(this.getProcedureName(), this.countParameters());

                Logger.debug("SQL > " + sql);

                try {

                    Connection dbConnection = tx.getConnection();

                    callableStatement = dbConnection.prepareCall(sql);

                    this.getFilter().setParametersInCallableStatement(callableStatement);

                    ResultSet rs = callableStatement.executeQuery();

                    resultDTO = ResultSetUtils.populateInList(classT, rs);

                    Ebean.commitTransaction();

                    Logger.debug("commitTransaction > " + sql);

                } catch (Exception e) {
                    Ebean.rollbackTransaction();

                    Logger.debug("rollbackTransaction > " + sql);

                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }finally{
                    Ebean.endTransaction();
                }

                return resultDTO;
            }

        ...

        }

Currently the support for stored procedures in Ebean is not orientated to what you are trying to do. 当前,Ebean中对存储过程的支持并未针对您要尝试的操作。 Hence you are not going to get much joy from using CallableSql or RawSql. 因此,使用CallableSql或RawSql不会给您带来很多乐趣。

>> a class to map the row (resultSet) I think that's the best way to work around performance and clarity >>映射行的类(resultSet)我认为这是解决性能和清晰度的最佳方法

Yes, I can understand your motivation. 是的,我可以理解您的动机。

>> How to convert ResultSet into model >>如何将ResultSet转换为模型

Currently there is no good solution. 当前没有好的解决方案。 The best solution would be to enhance RawSql so that you can set a ResultSet onto it. 最好的解决方案是增强RawSql,以便可以在其上设置ResultSet。 One of the things RawSql does is provide the mapping of resultSet columns to model properties and that is what Ebean needs internally. RawSql做的一件事是提供resultSet列到模型属性的映射,这是Ebean内部需要的。 The enhancement/code change would be to be able to set a resultSet onto the RawSql object ... and get Ebean internally to skip the creation of the resultSet ( preparedStatement, binding parameters and executeQuery()). 增强功能/代码更改将能够在RawSql对象上设置一个resultSet,并在内部获取Ebean来跳过resultSet的创建(prepareStatement,绑定参数和executeQuery())。 In terms of Ebean internals this is all done in the CQuery.prepareBindExecuteQueryWithOption() method. 就Ebean内部而言,这全部在CQuery.prepareBindExecuteQueryWithOption()方法中完成。 That is, if the RawSql has already provided a resultSet skip those things. 也就是说,如果RawSql已经提供了resultSet,请跳过这些事情。

The big benefit of doing this rather than just rolling your own row -> model mapping code is that the resulting beans would all still have lazy loading / partial object knowledge etc. They would behave exactly like any other beans that Ebean builds as part of it query mechanism. 这样做而不是仅仅滚动自己的行->模型映射代码的最大好处是,生成的bean仍然仍然具有延迟加载/部分对象知识等。它们的行为与Ebean为其构建的任何其他bean完全一样查询机制。

So that said, I'm personally away for a week ... so you aren't going to hear back from me until after that. 就是说,我个人待了一个星期……所以,直到那之后,您才不会收到我的回音。 If you want to get into it yourself then internally CQuery.prepareBindExecuteQueryWithOption() is the code you will need to modify. 如果您想自己入门,则内部需要使用CQuery.prepareBindExecuteQueryWithOption()这个代码。

If you have been following the ebean google group you'll know that but just in case you have not been note that the Model and Finder objects from Play have been incorporated into Ebean just in the last week. 如果您一直关注ebean谷歌小组,您会知道的,但是以防万一,您可能没有注意到Play的Model和Finder对象是在上周才合并到Ebean中的。 This helps both projects ... reduces confusion etc. The Ebean source in github master is at 4.0.4 and the bytecode enhancement in 4.x is different and I don't believe supported in Play. 这有助于两个项目...减少混乱等。github master中的Ebean源代码位于4.0.4,而4.x中的字节码增强功能则不同,并且我认为Play不支持。

I'm basically going offline for a week now so I'll look back into this after that. 我现在基本上已经下线了一个星期,所以之后我将对此进行回顾。

Cheers, Rob. 干杯,罗布。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM