Wednesday, 8 February 2012

PL/SQL Long Operation Progress Update

This code compiles and runs but there is a serious bug with it. The Java side doesn't seem to be receiving progress updates.

Stored Procedure For Main Operation

-- the output has to be a cursor so that Hibernate could deal with it
create or replace procedure test (out_cursor out sys_refcursor, v_context in number)
is
--the following two lines will cause Oracle insert a new row to the v$session_longops view
l_nohint number default dbms_application_info.set_session_longops_nohint;
l_rindex number default l_nohint;
--Oracle maintains this value
l_slno   number;
table_id number;
name_nonsense varchar(20);
v_i number;

begin
  table_id := 9;

-- a very long operation
  for i in 1 .. 400000
  loop
    name_nonsense := 'nonsense' || to_char(i);
    v_i := i;
   
    insert into nonsense
    values (nonsense_seq.nextval, name_nonsense, v_i, sysdate);
-- calling this procedure will update the respective row in the v$session_longops view
    dbms_application_info.set_session_longops
    ( rindex =>  l_rindex,
    slno   =>  l_slno,
    op_name => 'insertion of 400000 rows',
    target  =>  table_id,
    target_desc => 'inserting nonsense',
    context     => v_context,
    sofar       => i,
    totalwork   => 400000,
    units       => 'rows'
    );
  end loop;

  open out_cursor for
  select 'one' a, 'two' b from dual;

  exception
    when others then
      open out_cursor for
  select 'one1' a, 'two2' b from dual;

end test;


Progress Tracking Procedure
--although bloated, this procedure seems to be doing what it should
create or replace procedure updateProgress(ref_out out sys_refcursor, v_context in number)
is
v_serialnumber number;
v_remainingtime number;
v_elapsedtime number;
v_accomplishedpercentage number;
v_found boolean;
  
begin
  v_found := false;
    
  select 
  serial#, time_remaining , elapsed_seconds , (100 * sofar) / totalwork 
  into v_serialnumber, v_remainingtime, v_elapsedtime, v_accomplishedpercentage
  from v$session_longops
  where target = 9 and target_desc = 'inserting nonsense' and context = v_context  and time_remaining > 0;
  
  v_found := true;
  
  open ref_out for 
  select 
  v_serialnumber serialnumber, 
  v_remainingtime remainingtime, 
  v_elapsedtime elapsedtime, 
  v_accomplishedpercentage accomplishedpercentage 
  from dual;
  
  if ref_out%isopen then
    insert into nonsenselog values ('open' || to_char(v_elapsedtime), sysdate);
  else 
    insert into nonsenselog values ('closed' || to_char(v_elapsedtime), sysdate);
  end if;
  
  open ref_out for 
      select 
      v_serialnumber serialnumber, 
      v_remainingtime remainingtime, 
      v_elapsedtime elapsedtime, 
      v_accomplishedpercentage accomplishedpercentage 
      from dual;
  
  exception
    when too_many_rows then
      insert into nonsenselog values ('too many rows' || to_char(v_elapsedtime), sysdate);
    when no_data_found then
      if v_found then
        v_serialnumber := -1; 
        v_remainingtime := -1;
        v_elapsedtime := -1;
        v_accomplishedpercentage := -1;
      else 
        v_serialnumber := 0; 
        v_remainingtime := 0;
        v_elapsedtime := 0;
        v_accomplishedpercentage := 0;
        end if;
      open ref_out for 
      select 
      v_serialnumber serialnumber, 
      v_remainingtime remainingtime, 
      v_elapsedtime elapsedtime, 
      v_accomplishedpercentage accomplishedpercentage 
      from dual;
    when others then
      insert into nonsenselog values ('error' || to_char(v_elapsedtime), sysdate);
end updateProgress;

Progress.java
//this entity will represent the output produced by invocation of the progress tracking procedure
package com.sandahaung.testsp.entities;

import java.io.Serializable;
import javax.persistence.*;

/**
 *
 * @author Sandah
 */
@NamedNativeQueries({
    @NamedNativeQuery(name = "updateProgress",
    query = "call updateProgress(?, :context)",
    hints = {
        @QueryHint(name = "org.hibernate.callable", value = "true"),
        @QueryHint(name = "org.hibernate.cacheable", value = "false")
    },
    resultSetMapping = "progress")
})
@SqlResultSetMapping(name = "progress", entities = {
    @EntityResult(entityClass = com.sandahaung.testsp.entities.Progress.class, fields = {
        @FieldResult(name = "serialNumber", column = "serialnumber"),
        @FieldResult(name = "remainingTime", column = "remainingtime"),
        @FieldResult(name = "elapsedTime", column = "elapsedtime"),
        @FieldResult(name = "accomplishedPercentage", column = "accomplishedPercentage")
    })
})

@Entity
public class Progress implements Serializable {
    @Id
    private int serialNumber;
    private int remainingTime;
    private int elapsedTime;
    private double accomplishedPercentage;

    public double getAccomplishedPercentage() {
        return accomplishedPercentage;
    }

    public void setAccomplishedPercentage(double accomplishedPercentage) {
        this.accomplishedPercentage = accomplishedPercentage;
    }

    public int getElapsedTime() {
        return elapsedTime;
    }

    public void setElapsedTime(int elapsedTime) {
        this.elapsedTime = elapsedTime;
    }

    public int getSerialNumber() {
        return serialNumber;
    }

    public void setSerialNumber(int serialNumber) {
        this.serialNumber = serialNumber;
    }

    public int getRemainingTime() {
        return remainingTime;
    }

    public void setRemainingTime(int remainingTime) {
        this.remainingTime = remainingTime;
    }
}

ProgressUpdater.java
package com.sandahaung.testsp;

import com.sandahaung.testsp.entities.Progress;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.FlushModeType;
import javax.persistence.Persistence;
import javax.persistence.Query;

/**
 *
 * @author Sandah
 */
public class ProgressUpdater implements Runnable {

    int context;
    private EntityManagerFactory factory;
    private EntityManager em;

    public ProgressUpdater() {
        System.out.println("constructor run");
    }

    public ProgressUpdater(int context) {
        this.context = context;
    }

    public void run() {
        Progress progress = null;
        System.out.println("new thread entered...");
        try {
            System.out.println("code 1");
            Thread.sleep(5000);
        } catch (InterruptedException ex) {
            System.out.println("ex 1");
            //Logger.getLogger(ProgressUpdater.class.getName()).log(Level.SEVERE, null, ex);
        }
        do {
            try {
                System.out.println("code 2");
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                System.out.println("ex 2");
                //Logger.getLogger(ProgressUpdater.class.getName()).log(Level.SEVERE, null, ex);
            }
            progress = updateProgress();
            System.out.println("Elapsed time: " + progress.getElapsedTime());
            //System.out.println("Remaining time: " + progress.getRemainingTime());
            //System.out.println("Accomplished percentage: " + progress.getAccomplishedPercentage());
        } while (progress.getAccomplishedPercentage() != -1);
        em.close();
    }

    public Progress updateProgress() {
        Progress progress = findProgress(context);
        return progress;
    }

    public synchronized Progress findProgress(int context) {
        if (factory == null)
            factory = Persistence.createEntityManagerFactory("com.sandahaung_testsp_jar_1.0-SNAPSHOTPU");
        if (em == null)
            em = factory.createEntityManager();
        Query query = em.createNamedQuery("updateProgress");
        query.setParameter("context", context);
        query.setFlushMode(FlushModeType.AUTO);
        Progress progress = (Progress) query.getSingleResult();
        return progress;
    }
}

App.java
package com.sandahaung.testsp;

import com.sandahaung.testsp.entities.OneTwo;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;


public class App {

    public static void main(String[] args) {
        int context = 1;
        
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("com.sandahaung_testsp_jar_1.0-SNAPSHOTPU");
        EntityManager em = emf.createEntityManager();
        
        Query query = em.createNamedQuery("testCall");
        query.setParameter("context", context);
        
        new Thread(new ProgressUpdater(context)).start();
        
        OneTwo ot = (OneTwo) query.getSingleResult();
        
        System.out.println("a: " + ot.getA());
        System.out.println("b: " + ot.getB());
        
        em.close();
    }
}

Friday, 3 February 2012

Calling PL/SQL Stored Procedure from Hibernate

Stored Procedure

create or replace procedure test (out_cursor out sys_refcursor)
is
begin
  open out_cursor for
  select 'one' a, 'two' b from dual;
end test;

persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
  <persistence-unit name="com.sandahaung_testsp_jar_1.0-SNAPSHOTPU" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
      <property name="hibernate.connection.username" value="hr"/>
      <property name="hibernate.connection.driver_class" value="oracle.jdbc.OracleDriver"/>
      <property name="hibernate.connection.password" value="hradmin"/>
      <property name="hibernate.connection.url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
      <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
      <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle9iDialect"/>
      <property name="hibernate.hbm2ddl.auto" value="update"/>
    </properties>
  </persistence-unit>
</persistence>

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.sandahaung</groupId>
    <artifactId>testsp</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>testsp</name>
    <url>http://maven.apache.org</url>
    <repositories>
        <repository>
            <url>http://download.java.net/maven/2/</url>
            <id>hibernate-persistence</id>
            <layout>default</layout>
            <name>Repository for library Library[hibernate-persistence]</name>
        </repository>
    </repositories>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate</artifactId>
            <version>3.2.5.ga</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>ejb3-persistence</artifactId>
            <version>1.0.1.GA</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>3.3.2.GA</version>
        </dependency>
        <dependency>
            <groupId>javax.sql</groupId>
            <artifactId>jdbc-stdext</artifactId>
            <version>2.0</version>
        </dependency>
        <dependency>
            <groupId>javax.transaction</groupId>
            <artifactId>jta</artifactId>
            <version>1.0.1B</version>
        </dependency>
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc14</artifactId>
            <version>11.1.0.7.0</version>
        </dependency>
            
    </dependencies>
</project>


Entity
package com.sandahaung.testsp.entities;

import java.io.Serializable;
import javax.persistence.*;

/**
 *
 * @author Sandah
 */

@NamedNativeQueries({
    @NamedNativeQuery(name = "testCall",
    query = "call test(?)",
    hints = {@QueryHint(name = "org.hibernate.callable", value = "true")},
    resultSetMapping = "oneTwo")
})
@SqlResultSetMapping(name = "oneTwo", entities = {
    @EntityResult(entityClass = com.sandahaung.testsp.entities.OneTwo.class, fields = {
        @FieldResult(name = "a", column = "a"),
        @FieldResult(name = "b", column = "b")
    })
})
@Entity
public class OneTwo implements Serializable {

    @Id
    private String a;
    private String b;

    public String getA() {
        return a;
    }

    public void setA(String a) {
        this.a = a;
    }

    public String getB() {
        return b;
    }

    public void setB(String b) {
        this.b = b;
    }
}

main
package com.sandahaung.testsp;

import com.sandahaung.testsp.entities.OneTwo;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;


public class App {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("com.sandahaung_testsp_jar_1.0-SNAPSHOTPU");
        EntityManager em = emf.createEntityManager();
        
        Query query = em.createNamedQuery("testCall");
        
        OneTwo ot = (OneTwo) query.getSingleResult();
        
        System.out.println("a: " + ot.getA());
        System.out.println("b: " + ot.getB());
        
        em.close();
    }
}