Thursday, December 4, 2014

SQL query to get the details of Scheduled Concurrent Requests in Oracle applications

  SELECT r.request_id
       , p.user_concurrent_program_name
         || NVL2 (r.description
                , ' ('
                  || r.description
                  || ')'
                , NULL)
            conc_prog
       , s.user_name requestor
       , r.argument_text arguments
       , r.requested_start_date next_run
       , r.last_update_date last_run
       , r.hold_flag on_hold
       , r.increment_dates
       , DECODE (c.class_type,  'P', 'Periodic',  'S', 'On Specific Days',  'X', 'Advanced',  c.class_type) schedule_type
       , CASE
            WHEN c.class_type = 'P'
            THEN
               'Repeat every '
               || SUBSTR (c.class_info, 1, INSTR (c.class_info, ':') - 1)
               || DECODE (SUBSTR (c.class_info
                                ,   INSTR (c.class_info
                                         , ':'
                                         , 1
                                         , 1)
                                  + 1
                                , 1)
                        , 'N', ' minutes'
                        , 'M', ' months'
                        , 'H', ' hours'
                        , 'D', ' days')
               || DECODE (SUBSTR (c.class_info
                                ,   INSTR (c.class_info
                                         , ':'
                                         , 1
                                         , 2)
                                  + 1
                                , 1)
                        , 'S', ' from the start of the prior run'
                        , 'C', ' from the completion of the prior run')
            WHEN c.class_type = 'S'
            THEN
               NVL2 (dates.dates
                   , 'Dates: '
                     || dates.dates
                     || '. '
                   , NULL)
               || DECODE (SUBSTR (c.class_info, 32, 1), '1', 'Last day of month ')
               || DECODE (SIGN (TO_NUMBER (SUBSTR (c.class_info, 33)))
                        , '1', 'Days of week: '
                               || DECODE (SUBSTR (c.class_info, 33, 1), '1', 'Su ')
                               || DECODE (SUBSTR (c.class_info, 34, 1), '1', 'Mo ')
                               || DECODE (SUBSTR (c.class_info, 35, 1), '1', 'Tu ')
                               || DECODE (SUBSTR (c.class_info, 36, 1), '1', 'We ')
                               || DECODE (SUBSTR (c.class_info, 37, 1), '1', 'Th ')
                               || DECODE (SUBSTR (c.class_info, 38, 1), '1', 'Fr ')
                               || DECODE (SUBSTR (c.class_info, 39, 1), '1', 'Sa '))
         END
            AS schedule
       , c.date1 start_date
       , c.date2 end_date
       , c.class_info
    FROM apps.fnd_concurrent_requests r
       , apps.fnd_conc_release_classes c
       , apps.fnd_concurrent_programs_tl p
       , apps.fnd_user s
       , (WITH date_schedules
               AS (SELECT release_class_id, RANK () OVER (PARTITION BY release_class_id ORDER BY s) a, s
                     FROM (SELECT c.class_info
                                , l
                                , c.release_class_id
                                , DECODE (SUBSTR (c.class_info, l, 1), '1', TO_CHAR (l)) s
                             FROM (    SELECT LEVEL l
                                         FROM DUAL
                                   CONNECT BY LEVEL <= 31)
                                , apps.fnd_conc_release_classes c
                            WHERE c.class_type = 'S'
                              AND INSTR (SUBSTR (c.class_info, 1, 31), '1') > 0)
                    WHERE s IS NOT NULL)
              SELECT release_class_id, SUBSTR (MAX (SYS_CONNECT_BY_PATH (s, ' ')), 2) dates
                FROM apps.date_schedules
          START WITH a = 1
          CONNECT BY NOCYCLE PRIOR a = a - 1
            GROUP BY release_class_id) dates
   WHERE r.phase_code = 'P'
     AND c.application_id = r.release_class_app_id
     AND c.release_class_id = r.release_class_id
     AND NVL (c.date2, SYSDATE + 1) > SYSDATE
     AND c.class_type IS NOT NULL
     AND p.concurrent_program_id = r.concurrent_program_id
     AND p.language = 'US'
     AND dates.release_class_id(+) = r.release_class_id
     AND r.requested_by = s.user_id
ORDER BY conc_prog, on_hold, next_run;

Tuesday, October 14, 2014

FRM-92095: Oracle JInitiator version too low. Please install version 1.1.8.2 or higher

Resolved this Error By performing following Steps : 
Set the system environment variable, as follows :

  1.  Ensure that all browser windows are closed.
  2.  Access the Advanced System settings from the Control Panel.
  3.  On a Windows 7 client machine, this is done as follows : Navigate to the Control Panel, select the System item, select the Advanced system settings option.
   4. On a Windows XP client machine, this is done as follows : Navigate to the Control Panel, locate and open the System item, in the System Properties dialog, navigate to the Advanced tab.
    5. Select the Environment Variables button.
    6. Go through both the User variables box and the System variables box, looking for an existing variable called JAVA_TOOL_OPTIONS.
    7. Assuming no existing JAVA_TOOL_OPTIONS variable is found, select the New button in the System variables block, at the bottom of the screen.
    8.In the resultant New System Variable dialog, create a new variable with the following information:

Variable name  : JAVA_TOOL_OPTIONS
Variable value : -Djava.vendor="New Oracle"

(Note the leading hyphen "-" in the value)

   9. Select the OK button in the Environment Variables dialog, then the OK button in the System Properties dialog to save this change.

This works by changing the vendor information for Java from Oracle to anything else; I used the string "New Oracle" to avoid confusion; you can use the "Sun Microsystems Inc." string too.

This then stops the Java code in Forms 10g (specifically, the FRMALL.JAR server-side package, I believe) from thinking that the Java client is an older version of JInitiator, which is no longer supported - hence the confusing message.

Note that this problem does not occur in Oracle Forms 11g, as the relevant JAR package has been updated. However, you can run Oracle Forms 11g systems with this workaround in place.

Hope this resolve your issue,

Thursday, October 2, 2014

Unable to locate android build tools in the specified location (Oracle ADF Mobile)

If you are facing the error while deploying the Mobile app on the Andriod emulator and the error message says "Uable to locate android build tools in the specified location" in the Platform-tools Folder then the Andriod SDK version that you are trying to install on your local machine has the required files moved to the Build-tools --> 17.0.0 folder. To make it work you need to follow the following steps.

1. Open the Command Prompt as "Run As Administrator".
2. In the Commpand promt execute the Following commands

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.
C:\>mklink c:\AndroidSDK\sdk\platform-tools\aapt.exe C:\AndroidSDK\sdk\build-tools\17.0.0\aapt.exe

output for the Above Script :- symbolic link created for c:\AndroidSDK\sdk\platform-tools\aapt.exe <<===>> C:\AndroidSDK\sdk\build-tools\17.0.0\aapt.exe

C:\>mklink /D c:\AndroidSDK\sdk\platform-tools\lib c:\AndroidSDK\sdk\build-tools
\17.0.0\lib
output for the Above Script :- symbolic link created for c:\AndroidSDK\sdk\platform-tools\lib <<===>> c:\AndroidSDK\sdk\build-tools\17.0.0\lib


C:\>mklink c:\AndroidSDK\sdk\platform-tools\dx.bat C:\AndroidSDK\sdk\build-tools\17.0.0\dx.bat

output for the Above Script :- symbolic link created for c:\AndroidSDK\sdk\platform-tools\dx.bat <<===>> C:\AndroidSDK\sdk\build-tools\17.0.0\dx.bat


Once you are done with the above setup then you can try deploying Your mobile app to Andriod Emulator.


I Hope this will help you in getting rid of the Issue................

Saturday, September 13, 2014

Script To register the Custom Table In Oracle Application using AD_DD_REGISTER_TABLE


SET SERVEROUTPUT ON;

DECLARE
   l_appl_short_name                            VARCHAR2(40) := 'XXSSN';
   l_tab_name                                   VARCHAR2(32) := 'XXJG_ADDL_INFO_DFF_TBL';  -- Change the table name if you require
   l_tab_type                                   VARCHAR2(50) := 'T';
   l_next_extent                                NUMBER := 512;
   l_pct_free                                   NUMBER;
   l_pct_used                                   NUMBER;
BEGIN
   -- Unregister the custom table if it exists
   ad_dd.delete_table( p_appl_short_name => 'XXSSN', p_tab_name => l_tab_name);

   -- Register the custom table
   FOR tab_details IN (SELECT table_name
                            , tablespace_name
                            , pct_free
                            , pct_used
                            , ini_trans
                            , max_trans
                            , initial_extent
                            , next_extent
                         FROM dba_tables
                        WHERE table_name = l_tab_name)
   LOOP
      DBMS_OUTPUT.put_line(
            'Registering Table : '
         || l_tab_name);
      ad_dd.register_table(
         p_appl_short_name                         => l_appl_short_name
       , p_tab_name                                => tab_details.table_name
       , p_tab_type                                => l_tab_type
       , p_next_extent                             => NVL(tab_details.next_extent, 512)
       , p_pct_free                                => NVL(tab_details.pct_free, 10)
       , p_pct_used                                => NVL(tab_details.pct_used, 70));
   END LOOP;

   -- Register the columns of custom table
   FOR all_tab_cols IN (SELECT column_name
                             , column_id
                             , data_type
                             , data_length
                             , nullable
                          FROM dba_tab_columns
                         WHERE table_name = l_tab_name)
   LOOP
      DBMS_OUTPUT.put_line(
            'Registering Column : '
         || all_tab_cols.column_name);
      ad_dd.register_column(
         p_appl_short_name                         => l_appl_short_name
       , p_tab_name                                => l_tab_name
       , p_col_name                                => all_tab_cols.column_name
       , p_col_seq                                 => all_tab_cols.column_id
       , p_col_type                                => all_tab_cols.data_type
       , p_col_width                               => all_tab_cols.data_length
       , p_nullable                                => all_tab_cols.nullable
       , p_translate                               => 'N'
       , p_precision                               => NULL
       , p_scale                                   => NULL);
   END LOOP;

   FOR all_keys IN (SELECT constraint_name
                         , table_name
                         , constraint_type
                      FROM all_constraints
                     WHERE constraint_type = 'P'
                       AND table_name = l_tab_name)
   LOOP
      ad_dd.register_primary_key(
         p_appl_short_name                         => l_appl_short_name
       , p_key_name                                => all_keys.constraint_name
       , p_tab_name                                => all_keys.table_name
       , p_description                             => 'Register primary key'
       , p_key_type                                => 'S'
       , p_audit_flag                              => 'N'
       , p_enabled_flag                            => 'Y');

      FOR all_columns IN (SELECT column_name
                               , position
                            FROM dba_cons_columns
                           WHERE table_name = all_keys.table_name
                             AND constraint_name = all_keys.constraint_name)
      LOOP
         ad_dd.register_primary_key_column(
            p_appl_short_name                         => l_appl_short_name
          , p_key_name                                => all_keys.constraint_name
          , p_tab_name                                => all_keys.table_name
          , p_col_name                                => all_columns.column_name
          , p_col_sequence                            => all_columns.position);
      END LOOP;
   END LOOP;

   COMMIT;
EXCEPTION
   WHEN OTHERS
   THEN
      DBMS_OUTPUT.put_line(
            'UNEXP_ERROR : '
         || SUBSTR( SQLERRM, 1, 250));
END;
/

Tuesday, September 9, 2014

Script to Explode the DBA_DEPENDENCIES Hierachy till Nth Level

    SELECT DISTINCT referenced_name
                  , referenced_type
                  , LEVEL
                  ,    LPAD( ' ', 2 * (LEVEL - 1))
                    || name
                  , owner
                  , name
                  , TYPE
      FROM (SELECT *
              FROM dba_dependencies
             WHERE 1 = 1
               AND (referenced_name LIKE 'XX%'
                 OR referenced_name LIKE 'XX_1%')
               AND (name LIKE 'XX_%'
                 OR name LIKE 'XX_1%')
               AND name <> referenced_name)
START WITH referenced_name =<<&p_object_name>>
CONNECT BY NOCYCLE referenced_name = PRIOR name
  ORDER BY LEVEL

Tuesday, August 26, 2014

How to Handle the Date Parameter for Value set (FND_STANDARD_DATETIME) in Oracle R12

1. Define the Concurrent Program With Parameter as Date and attach the FND_STANDARD_DATETIME and in the PLSQL Block pass the Parameter to the Function defined below.

DECLARE
errbuf varchar2(240) := NULL;
retcode number := 0;
p_date varchar2(30) := '&1';

input_date date := FND_CONC_DATE.STRING_TO_DATE('&1');
BEGIN
FND_FILE.PUT_LINE(FND_FILE.OUTPUT, 'Writing to output file' );
FND_FILE.PUT_LINE(FND_FILE.OUTPUT, 'Parameter 1 = ' || nvl(p_date,'NULL'));
FND_FILE.PUT_LINE(FND_FILE.OUTPUT, 'Converted Date is '||input_date);
FND_FILE.PUT_LINE(FND_FILE.LOG, 'Writing to Logfile to test Date Parameters to
SQL*Plus');
FND_FILE.PUT_LINE(FND_FILE.LOG, 'Parameter 1 = ' || nvl(p_date,'NULL'));
FND_FILE.PUT_LINE(FND_FILE.LOG, 'Converted Date is '||input_date);
END;
/


PL/SQL Procedure

CREATE OR REPLACE PROCEDURE simple_pls (
errbuf out varchar2,
retcode out varchar2,
in_date in varchar2
)
is
input_date date;
begin
input_date := FND_CONC_DATE.STRING_TO_DATE(in_date);
fnd_file.put_line (fnd_file.output,'TEST to OUTPUT file');
fnd_file.put_line (fnd_file.output,'Original Date is '||in_date);
fnd_file.put_line (fnd_file.output,'Converted Date is '||input_date);
fnd_file.put_line (fnd_file.log,'Test to LOG File');
fnd_file.put_line (fnd_file.log,'Original Date is '||in_date);
fnd_file.put_line (fnd_file.log,'Converted Date is '||input_date);
end;
/

Saturday, August 23, 2014

Script to Compile Invalid Objects in a Schema


This procedure recompile invalid objects in a given schema or all invalid objects in the database.

Parameter :
 schema     (IN) - Schema in which to recompile invalid objects  If NULL, all invalid objects in the database are recompiled.

BEGIN
UTL_RECOMP.recomp_serial(p_schema_name);
END;

DESCRIPTION:
This procedure is the main driver that recompiles invalid objects
in the database (or in a given schema) in parallel in dependency
order. It uses information in dependency$ to order recompilation
of dependents after parents.

NOTES:
The parallel recompile exploits multiple CPUs to reduce the time taken to recompile invalid objects. However, please note that recompilation writes significant amounts of data to system tables,
so the disk system may be a bottleneck and prevent significant speedups.

   PARAMETERS:
Threads    (IN) - Number of recompile threads to run in parallel If NULL, 0, or negative, RECOMP_PARALLEL computes a default degree of parallelism as the product of Oracle parameters "cpu_count" and "parallel_threads_per_cpu". On a Real Application Clusters installation, the degree of parallelism is the sum of individual settings on each node in the cluster.
Schema     (IN) - Schema in which to recompile invalid objects If NULL, all invalid objects in the database
                         are recompiled.
Flags      (IN) - Option flags supported (as described above).
   

BEGIN
UTL_RECOMP.recomp_parallel(p_schema_name);
END;

Tuesday, August 19, 2014

How to use comma separated string build dynamically and pass to IN clause of select statement


In some cases, we get a comma separated string as output (say from another select statement) that we would need to pass to the IN clause of a select statement.

This article explains how to achieve that using regexp_substr (DB >=10g).

For example, assume a select statement returns the following
'SMITH,ALLEN,WARD,JONES'

Now, we would need to pass this to another select statement as IN clause and get the output.

SQL> select * from emp where ename in ('SMITH,ALLEN,WARD,JONES');

no rows selected

Well, this is not our expected output. We expect the query to return 4 rows.
This can be achieved by splitting the comma separated string to individual strings and pass it to the IN clause.

Oracle provides regexp_substr function, which comes handy for this scenario.
First, we will form a query, that splits this comma separated string and gives the individual strings as rows.

 

SQL> select regexp_substr('SMITH,ALLEN,WARD,JONES','[^,]+', 1, level) from dual
  2  connect by regexp_substr('SMITH,ALLEN,WARD,JONES', '[^,]+', 1, level) is not null;

REGEXP_SUBSTR('SMITH,A
----------------------
SMITH
ALLEN
WARD
JONES

The above query iterates through the comma separated string, searches for the comma (,) and then splits the string by treating the comma as delimiter. It returns the string as a row, whenever it hits a delimiter.

 

We can pass this query to our select statement to get the desired output.

 

SQL> select * from emp where ename in (
  2  select regexp_substr('SMITH,ALLEN,WARD,JONES','[^,]+', 1, level) from dual
  3  connect by regexp_substr('SMITH,ALLEN,WARD,JONES', '[^,]+', 1, level) is not null );

 

EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
      7369 SMITH      CLERK           7902 17-DEC-80        800                    20
      7499 ALLEN      SALESMAN        7698 20-FEB-81       1600        300         30
      7521 WARD       SALESMAN        7698 22-FEB-81       1250        500         30
      7566 JONES      MANAGER         7839 02-APR-81       2975                    20

Now, the query returns what we expected.

Thursday, July 24, 2014

ORA-22160: element at index [9] does not exist

ORA-22160 Error For FORALL

We have been just amazed at how much better our programs perform when we use FORALL to do our inserts and updates. We are now building a new application on Oracle Database 10gRelease 2, and we have run into a problem. In all previous usages of FORALL, we would generally take a collection that was populated with a BULK COLLECT and push it into one or more tables.
Now we have a more complicated scenario, in which we must go through our collection of "candidate" data for inserts and remove some (perhaps all) of the rows before doing the insert. When we try to use FORALL, we get this error message: 
ORA-22160: element at index [2750] does
not exist

How can we avoid this error and get all our data inserted?
I agree that FORALL is wonderful—one of the most important enhancements to PL/SQL since Oracle8i was released. And back in Oracle8i and Oracle9i Database, it is true that the only format with which you could use FORALL was this:  
FORALL index_variable
IN low_value .. high_value
   <DML_Statement>;

And as in a "regular" numeric FOR loop, FORALL will iterate through each integer between low_value and high_value , using that integer to identify an element in all collections that are bound into the DML statement with the index_variable . If no element exists at a particular index value, Oracle Database raises an exception, as you can see in the example in Listing 3.

Code Listing 3: Raising ORA-22160 

DECLARE
   TYPE list_of_names_t IS TABLE OF VARCHAR2(32767)
      INDEX BY PLS_INTEGER;

   happyfamily                                  list_of_names_t;
BEGIN
   happyfamily( 1)                                          := 'Eli';
   happyfamily( 2)                                          := 'Chris';
   happyfamily( 3)                                          := 'Veva';
   happyfamily( 5)                                          := 'Steven';

   FORALL indx IN happyfamily.FIRST .. happyfamily.LAST
      INSERT INTO first_names
           VALUES (happyfamily( indx));
END;
/
DECLARE
*
ERROR at line 1:
ORA-22160: element at index [4] does not exist

FORALL, in other words, requires a sequentially or densely filled collection. Now if you were still running Oracle8i or Oracle9i Database and wanted to fix this problem, you would have to copy the data from your sparsely filled collection over to one without any gaps. From a performance standpoint, this is nothing to worry about; manipulating collections is very fast. But it does involve writing and maintaining even more code.
In Oracle Database 10g, Oracle added two new clauses to the FORALL statement: INDICES OF and VALUES OF. They allow you to avoid the restriction on using densely filled collections. Instead of using an IN range of values, you can point to a collection (usually, but not necessarily, the same collection that is referenced inside the FORALL's DML statement) and say, in effect, "Use only those index values defined in that other collection" (INDICES OF) or "Use only index values that are found in the elements of that other collection" (VALUES OF).
Here is a rewrite of the code in Listing 3 that avoids the ORA-22160 error (notice the boldfacedlines): 

DECLARE
  TYPE list_of_names_t
  IS TABLE OF VARCHAR2 (32767)
     INDEX BY PLS_INTEGER;

  happyfamily   list_of_names_t;
BEGIN
  happyfamily (1) := 'Eli';
  happyfamily (2) := 'Chris';
  happyfamily (3) := 'Veva';
  happyfamily (5) := 'Steven';

  FORALL indx
  IN INDICES OF happyfamily
    INSERT INTO first_names
         VALUES (happyfamily (indx));
END;
/

That is an example of the simplest way to apply INDICES OF: "self- reference" the same collection used within the DML statement, to easily avoid errors due to sparseness in that collection.
Now let's take a look at VALUES OF. This clause comes in very handy when you want to use only a subset of the collection to be used within the DML statement.
Suppose, for example, that I have a procedure that accepts a collection of employee records and should insert only records for employees with a salary of $10,000 or more. Listing 4 contains the package specification and the body for this employees_dml program.
Code Listing 4: Package and body for employees_dml 

CREATE OR REPLACE PACKAGE employees_dml
IS
   TYPE employees_aat IS TABLE OF employees%ROWTYPE
      INDEX BY PLS_INTEGER;

   PROCEDURE insert_some (employees_in IN employees_aat);
END employees_dml;
/

CREATE OR REPLACE PACKAGE BODY employees_dml
IS
   PROCEDURE insert_some( employees_in IN employees_aat)
   IS
      TYPE index_values_aat IS TABLE OF PLS_INTEGER
         INDEX BY PLS_INTEGER;

      l_values_of                                  index_values_aat;
      l_index                                      PLS_INTEGER;
   BEGIN
      l_index                                                  := employees_in.FIRST;

      WHILE (l_index IS NOT NULL)
      LOOP
         IF employees_in( l_index).salary >= 10000
         THEN
            l_values_of( l_values_of.COUNT + 1)                      := l_index;
         END IF;

         l_index                                                  := employees_in.NEXT( l_index);
      END LOOP;

      FORALL indx IN VALUES OF l_values_of
         INSERT INTO employees
              VALUES employees_in( indx);
   END insert_some;
END employees_dml;

Lines 5 through 9 in Listing 4 declare the VALUES OF collection, a collection of PLS_INTEGER values. Then in my WHILE loop (lines 14 through 22), I populate a row in l_values_of with the index value from employees_in, only if the salary in that record is at least $10,000.
Thus, when I get to the FORALL statement (lines 24 through 26), the VALUES OF clause ensures that all other employee records are ignored.
If you have the standard Oracle employees table installed with default data, you can run the script in Listing 5 to verify the behavior of the employees_dml package.

Code Listing 5: Verifying behavior of employees_dml package 

SELECT COUNT(*)
  FROM employees
WHERE salary < 10000
/

DECLARE
   l_employees                   employees_dml.employees_aat;
BEGIN
   SELECT *
   BULK COLLECT INTO l_employees
     FROM employees;

   DELETE FROM employees;

   employees_dml.insert_some (l_employees);
END;
/

SELECT COUNT(*)
  FROM employees
WHERE salary < 10000
/

ROLLBACK
/

  COUNT(*)
-------------------------
            88

1 row selected.
PL/SQL procedure successfully completed.


  COUNT(*)
-------------------------
             0

1 row selected.
Rollback complete.

Finally, you can also use INDICES OF with an entirely different collection that serves as a kind of filter for the collections used in the DML statement.
Listing 6 shows an example of this approach.
Code Listing 6: Using INDICES OF as a filter 

DECLARE
   TYPE employee_aat IS TABLE OF employees.employee_id%TYPE
      INDEX BY PLS_INTEGER;

   l_employees                                  employee_aat;

   TYPE boolean_aat IS TABLE OF BOOLEAN
      INDEX BY PLS_INTEGER;

   l_employee_indices                           boolean_aat;
BEGIN
   l_employees( 1)                                          := 137;
   l_employees( 100)                                        := 126;
   l_employees( 500)                                        := 147;
   --
   l_employee_indices( 1)                                   := FALSE;
   l_employee_indices( 500)                                 := TRUE;
   l_employee_indices( 799)                                 := NULL;

   --
   FORALL l_index IN INDICES OF l_employee_indices BETWEEN 1 AND 500
      UPDATE employees
         SET salary                                                    = 10000
       WHERE employee_id = l_employees( l_index);
END;

In this code, I use the index values of defined elements in the l_employee_indices collection to specify which elements of the l_employees collection to use in my update statement. Note that on line 21 of Listing 6, I include a BETWEEN clause to constrain which of the index values of l_employee_indices will be used. So INDICES OF should fix your problem in this case.
Using Encapsulation Without Privileges
I have taken to heart your advice about writing SQL statements (don't write SQL in application-level code; hide it behind a packaged API, with as much of it generated as possible). I also decided (and I am a team leader, so my decision carries some weight) to go the full route, and I revoked privileges on the tables, so my developers have no choice but to use the encapsulation packages.
Here's my problem: another of your best practices is to avoid hard-coded declarations and to anchor variables back to database tables and columns with %TYPE and %ROWTYPE. But my people can't do that, because I revoked privileges and they need the SELECT privilege on a table to do that.
What's a best-practice-oriented guy supposed to do?
So nice to hear that you are going the encapsulation route! I have used it myself in recent years in every single one of my development projects, and I really can't imagine going back to the "old way" of writing all the SQL statements every time I need them.
And I am very impressed to hear about your decision to revoke table privileges. That's a hard thing to do, but once the revocation is in place, it can greatly improve the robustness of your application.
Yet, as you point out, it leads to an interesting conflict of best practices.
Here's how I resolve this problem: I generate three table API packages for each of my tables: 
·         A change package that implements all the basic INSERT, UPDATE, and DELETE operations
·         A query package that gives me a wide range of functions for querying data from the table
·         A types package that creates a set of subtypes , which in essence hide the %TYPE and %ROWTYPE declarations and ensure that I don't even need SELECT authority on tables to build high-quality code
How does it work? Simple.
Suppose I am building an application to maintain a category of things my company sells. One of my tables is cat_tools, which contains information about tools. The table has a name, a description, and a universal_id (primary key, defined as a GUID, or globally unique identifier). Listing 7, includes a portion of the types package for this table.
Code Listing 7: Creating a type package as an API 

CREATE OR REPLACE PACKAGE cat_tools_tp
IS
   SUBTYPE cat_tools_rt IS cat_tools%ROWTYPE;

   SUBTYPE universal_id_t IS cat_tools.universal_id%TYPE;

   SUBTYPE name_t IS cat_tools.NAME%TYPE;

   SUBTYPE description_t IS cat_tools.description%TYPE;

   TYPE table_refcur IS REF CURSOR
      RETURN cat_tools%ROWTYPE;

   TYPE cat_tools_tc IS TABLE OF cat_tools%ROWTYPE
      INDEX BY PLS_INTEGER;

   TYPE universal_id_cc IS TABLE OF cat_tools.universal_id%TYPE
      INDEX BY BINARY_INTEGER;
END cat_tools_tp;
/

So now assume that the cat_tools table and both the cat_tools_tp (types) and cat_tools_qp (query) packages are defined in the CATALOG schema. I grant EXECUTE on cat_tools_tp and cat_tools_qp to HR. Then from HR, I can write the block of code in Listing 8.
Code Listing 8: Access using the type and query packages 

DECLARE
   /* A string to hold the assertion name */
   l_name CATALOG.cat_tools_tp.name_t;
  
   /* A collection to hold the set of tools rows. */
   l_tools CATALOG.cat_tools_tp.cat_tools_tc;
BEGIN
   /* The allrows function retrieves all rows from cat_tools. */
   l_tools := CATALOG.cat_tools_qp.allrows;

   /* Assign each name from collection to local variable. */
   FOR indx IN 1 .. l_tools.COUNT
   LOOP
      l_name := l_tools (indx).NAME;
   END LOOP;
END;
/

As you can see, even though the HR schema has no access to the cat_tools table, I am able to declare variables by using datatypes that are anchored (indirectly) to the table and its columns. I can also query the contents of the table.

And if the table ever changes, the types package will also change (that is, you will regenerate it). All programs that reference this package will then have to be recompiled; thus, you have the same dependencies you would have with %ROWTYPE and %TYPE declarations.