Удаленная работа и как правильно ее готовить

Речь пойдет об организации удаленки на постоянном месте работы, не о фрилансе.
Ни для кого не секрет, что работодатели очень любят, чтобы их сотрудники находились в офисе и очень не любят, когда те работают удаленно. Происходит это от недоверия: не видно человека — значит непонятно, что он делает. В моей практике мне приходилось слышать от руководства такое: «Откуда нам знать, что ты работаешь? Может ты в приставку играешь?”
Чтобы таких вопросов не возникало и все были довольны я придерживаюсь следующих принципов:
  1. Начинать и заканчивать рабочий день тогда же когда и в офисе
    Если рабочий день в офисе начинается в 9:00, то и работая удаленно в 9:00 надо быть в онлайне. Из этого могут быть исключения, т.к. удаленная работа предоставляет определенную гибкость в планировании своего времени, но тогда надо помнить что 8 часов в день в любом случае принадлежат вашей работе
  2. Время отклика — 10 минут
    Это означает, что если коллеги, например через мессенджер, задают вопрос, их вопрос не проваливается в пустоту и они получают от вас отклик. Это не значит, что надо бросить ту задачу которая в текущий момент выполняется, достаточно ответить “я сейчас работаю над такой-то задачей, через Х минут отвечу на ваш вопрос”. 10 минут вполне достаточно, чтобы зафиксировать свою текущую мысль и ответить. Таким образом люди видят, что вы работаете и вы на связи. Вас не должны «искать». Если вам нужно куда-то отлучиться, просто поставьте коллег в известность, через какой период времени вы будете на связи.
  3. Всегда надо быть готовым отчитаться о ходе работы
    Тут все очень просто: у вас спрашивают, чем вы сейчас заняты, вы отвечаете, что конкретно вы делаете, например “Я по такой-то задаче делаю то-то”. Тут главное сообщать не просто задачу, а какое  действие по ней вы сейчас совершаете.
  4. Если возникают проблемы в выполнении задачи, сразу сообщать о  них
    Тут все очевидно. Непонятна задача? Нехватает данных? Надо сразу “бить в колокола” а не дожидаться когда вас спросят почему задача не выполнена. Это относится к работе вообще, а не только к удаленке.
Кому-то может показаться, что перечисленные мной правила в какой-то степени ограничивают свободу, присущую удаленке, но тут следует помнить, что удаленная работа — такая же работа как и в офисе, разница только в том, что вы не тратите 2+ часа в день на дорогу и можете сами организовывать свое рабочее место. Эти правила являются компромиссным вариантом, другим вариантом может быть, например, использование в организации приложений слежения/учета времени удаленных сотрудников о которых я возможно расскажу позже.
Реклама
Рубрика: Uncategorized | Оставить комментарий

Вызов хранимой PL/SQL-функции из ADF

Наконец-то разобрался как вызывать хранимые PL/SQL процедуры/функции из ADF, и для тех кто тоже хочет разобраться, но еще этого не сделал (а также для себя, чтобы не забыть), я написал это пошаговое руководство. За основу приложения взят пример из учебника “Developing Rich Web Applications With Oracle ADF” для стандартной демо-схемы БД Oracle HR.

1) В БД добавляем простую функцию (я решил подсчитать сумму зарплаты по департаменту):

create or replace function get_sum_salary_by_dept 
(
  p_dept_id in number 
) return number as 
  v_result number;
begin
  select sum(salary)
    into v_result
    from employees
    where department_id = p_dept_id;
  return v_result;
end get_sum_salary_by_dept;

2) Создаем имплементацию Application Module:
2

3) В получившийся java-класс добавляем общий метод (для удобства я его сделал статическим) запуска хранимых функций:

    // Some constants
        public static int NUMBER = Types.NUMERIC;
        public static int DATE = Types.DATE;
        public static int VARCHAR2 = Types.VARCHAR;

    //Делаем метод статическим и добавляем передачу в него транзакции
    /* В import надо добавить
       import java.sql.CallableStatement;
       import java.sql.SQLException;
       import java.sql.Types;
       import oracle.jbo.JboException;
       import oracle.jbo.server.DBTransaction;
    */
    public static Object callStoredFunction(DBTransaction tr, int sqlReturnType, String stmt,
                                            Object[] bindVars) {
      CallableStatement st = null;
      try {
        // 1. Create a JDBC CallabledStatement  
        st = tr.createCallableStatement(
               "begin ? := "+stmt+";end;",0);
        // 2. Register the first bind variable for the return value
        st.registerOutParameter(1, sqlReturnType);
        if (bindVars != null) {
          // 3. Loop over values for the bind variables passed in, if any
          for (int z = 0; z < bindVars.length; z++) {
            // 4. Set the value of user-supplied bind vars in the stmt
            st.setObject(z + 2, bindVars[z]);
          }
        }
        // 5. Set the value of user-supplied bind vars in the stmt
        st.executeUpdate();
        // 6. Return the value of the first bind variable
        return st.getObject(1);
      }
      catch (SQLException e) {
        throw new JboException(e);
      }
      finally {
         if (st != null) {
            try {
              // 7. Close the statement
              st.close();
            }
            catch (SQLException e) {
               throw new JboException(e);
            }
         }
      }
   }

4) Генерируем класс для объекта сущности:

4

5) В получившийся класс добавляем метод:

public Number callGetSumSalaryByDept() {
      Number n = this.getDepartmentId();
      return (Number)AppModuleImpl.callStoredFunction(getDBTransaction(),
               AppModuleImpl.NUMBER, «get_sum_salary_by_dept(?)», new Object[]{n});
}

 

6) Генерируем классы для объекта представления и строки представления:

6

7) В класс объекта представления (в моем примере DepartmentsViewImpl.java) добавляем метод, вызывающий метод класса сущности:

    public Float getDepartmentSalarySum(Row row){
        DepartmentsViewRowImpl currentRow = (DepartmentsViewRowImpl)row;
        DepartmentsImpl departmentEntity = currentRow.getDepartments();

        Float res = departmentEntity.callGetSumSalaryByDept().floatValue();
        return res;
    }

 

8) В свойствах представления, на вкладке Java, в разделе Client Interface, кликаем кнопку редактирования и, в открывшемся окне, переносим наш метод в Selected:

8

9) С моделью на этом все. Нажимаем Save All и Rebuid All и переходим к интерфейсу

10) Для управления я взял основную страницу приложения (в моем примере DeptEmpPage.jsf). В AppModuleDataControl присутствует наш метод:

11

11) Из палитры компонентов бросаем на страницу кнопку (Button):

11_

12) У отмеченной кнопки нажимаем на выпадающее меню (Button Actions, справа от кнопки) и выбираем Bind to ADF Control в котором выбираем наш метод:

12

После этого откроется окно Edit Action Binding:

12-2

В этом окне, в параметрах, в колонке Value, нужно открыть выпадающий список и выбрать Show El Expression Builder. В открывшемся окне выбрать источник откуда параметр получит строку (в моем примере текущая строка):
12-3

Нажимаем OK везде, где оно есть

13) В свойствах кнопки, в поле Text переименовываем ее в что-либо более человечное Подмигивающая рожица

14) Из Data Control кидаем возвращаемое значение метода на страницу и выбираем Text –> ADF Output Text w/ Label (можем сразу и изменить label):
14

15) Теперь создадим Partial trigger, чтобы после вызова функции, в компоненте Output Text отобразилось возвращаемое значение. Для этого в свойствах текста (Output Text), в разделе Behavior, нажимаем правую кнопку на свойстве Partial Triggers и выбираем Edit. В открывшемся окне переносим, созданную ранее кнопку в Selected:
15
Нажимаем OK

16) Сохраняем и запускаем наше приложение. После запуска нажимаем кнопку и наслаждаемся результатом:
16

Ссылки:
Мой готовый пример
руководство Developing Rich Web Applications With Oracle ADF
раздел документации Developing Fusion Web Applications with Oracle Application Development Framework – 16 Extending Business Components Functionality

Рубрика: Development, Oracle | Метки: , , , | 2 комментария

Создание внешних ключей

Из найденого в черновиках. Данный скрипт применялся для докручивания БД, где внешнии ключи отсутствовали как класс
и связи между таблицами строились на основе наименования полей.

declare
  vSQL varchar2(2000);
  vi number;
begin
  vi := 1;
for c in (
select a.table_name, a.column_name, b.table_name pk_table
  from user_tab_columns a,
       user_constraints b,
       user_ind_columns c
  where (a.column_name like 'KD_%' or (a.column_name like 'ID_%' and a.column_name!='ID_USR'))
    and not exists (select 1 from user_constraints b1, user_ind_columns c1
                      where b1.table_name=a.table_name and b1.constraint_type='P' and c1.index_name=b1.index_name
                        and c1.column_name=a.column_name)
    and b.constraint_type='P' and b.table_name!=a.table_name and b.index_name=c.index_name
    and c.column_name = a.column_name) loop
    begin
      vSQL:='create index '||c.table_name||'_i'||to_char(vi)||' on '||c.table_name||' ('||c.column_name||')';
      execute immediate vSQL;
      vi:=vi+1;
    exception
      when others then
        null;
    end;
    begin
      vSQL:='alter table '||c.table_name||' add constraint '||c.table_name||'_fk'||to_char(vi)||' foreign key ('||c.column_name||') '||
            'references '||c.pk_table||' ('||c.column_name||')';
      execute immediate vSQL;
      vi:=vi+1;
    exception
      when others then
        dbms_output.put_line(c.table_name||c.column_name);
        dbms_output.put_line(vSQL);
    end;
end loop;
end;
Рубрика: Uncategorized | Оставить комментарий

Dark Theme for Oracle JDeveloper/SQL Developer

Основа взята отсюда и немного (на текущий момент чуть менее чем полностью) переделана. Для установки кладем файл fusiondark.jar (тут) в .\ide\themes\ . В преференсах выбираем тему «Fusion Dark»:

SQLDev_dark_sw

Результат:

SQLDev_dark2

UPD: v2 почти вычищен синий цвет

Рубрика: Uncategorized | 4 комментария

Типовая задача с собеседований

На которой я почему-то залипаю (при том что знаю решение):

Есть таблица:

create table t
  (id number,
   field_name varchar2(20),
   string_value varchar2(20),
   number_value number);
  
insert into t (id, field_name, string_value, number_value)
  values (1, ‘Name’, ‘John’, null);
insert into t (id, field_name, string_value, number_value)
  values (1, ‘Surname’, ‘Smith’, null); 
insert into t (id, field_name, string_value, number_value)
  values (1, ‘Salary’, null, 100); 
 
insert into t (id, field_name, string_value, number_value)
  values (2, ‘Name’, ‘Bill’, null);
insert into t (id, field_name, string_value, number_value)
  values (2, ‘Surname’, ‘Dow’, null); 
insert into t (id, field_name, string_value, number_value)
  values (2, ‘Salary’, null, 200);   
commit;

надо ее “развернуть” на 90 градусов.

решение простое:

select t.id,
       max(decode(field_name, ‘Name’, string_value)) Name,
       max(decode(field_name, ‘Surname’, string_value)) Surname,
       max(decode(field_name, ‘Salary’, number_value)) Salary
  from t
  group by t.id;

Рубрика: Oracle | Метки: | Оставить комментарий

Отладка приложения из OC4J в IDEA

Если у вас приложения для OC4J сделано в JDeveloper 10g то проблем нет – JDeveloper сам понимает контейнер и можно отлаживаться. Но что делать, если вам достается проект, собиреемый Maven’ом и содержащий в себе весь “зоопарк” Java-технологий (Spring, Hibernate и т.д.). Конфигурация для OC4J в IDEA по-умолчанию отсутствует. Поиск статей по этой проблеме на русском языке результатов не дал. Поэтому напишу свою. Все действия происходят под Windows.

Шаг 1. Настраиваем локальный OC4J контейнер

В файл oc4j.cmd вставляем:

set OC4J_JVM_ARGS=-Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n -Xms512m -Xmx700m -XX:MaxPermSize=500m
set JVMARGS=%OC4J_JVM_ARGS%

где address=8000 – адрес порта для отладки (это не тот порт к которому идет подключение клиентом или браузером), а -Xms512m -Xmx700m -XX:MaxPermSize=500m – настройки памяти JVM (что они обозначают не имею ни малейшего представления, потому что не настоящий java-программер)

Запускаем: oc4j –start

По-умолчанию порт выставлен как 8888, заходим в Server Control: http;//localhost:8888/em и деплоим предварительно собраный ear-файл.

Шаг 2. Настраиваем IDEA

Открываем проект (если проект сделан в Maven и не создан проект для IDEA, то сначала запускаем mvn idea:idea, а потом открываем получившийся проект)

В IDEA выбираем Run –> Edit configuration. В открывшимся окне нажимаем кнопку “Add new configuration”, в списке выбираем Remote, обзываем по человечески и меняем номер порта на тот который задан в шаге 1 у контейнера (8000)

image

Сохраняем.

Шаг 3. Собственно отладка

Расставляем брек-пойнты, жмем Run –> Debug ‘OC4J’. Подключаемся к контейнеру клиентом, выполняем операции, которые остановятся на брек-пойнте и будет предоставлена отладочная информация.

Вот в общем-то и все.

Рубрика: Uncategorized | Оставить комментарий

Отключение триггеров и констрейнтов

Делал это для подгрузки данных в Oracle из дампов. Может кому пригодится.

Сначала создаем 2 таблицы:

create table tmp_dbobj 
  (name varchar2(50), 
   otype varchar2(50));
   
create table tmp_cons_log 
  (cname varchar2(50), 
   ctype varchar2(1), 
   tname varchar2(50), 
   err_text varchar2(2000), 
   cols varchar2(1000), 
   vals varchar2(1000));

Отключаем:

--Выключение констрейнтов
declare
  vDDL varchar2(2000);
begin
  --triggers
  for c_trg in (select * from user_triggers where status = 'ENABLED') loop
    vDDL := 'alter trigger '||c_trg.trigger_name||' disable';
    execute immediate vDDL;
    insert into tmp_dbobj (name, otype)
      values (c_trg.trigger_name, 'trigger');
    commit;
  end loop;
  --FK
  for c_cons in (select * from user_constraints 
                 where constraint_type in ('R') and status = 'ENABLED'
                   --and constraint_name not like 'SYS%'
                 ) loop
    vDDL := 'alter table '||c_cons.table_name||' disable constraint '||c_cons.constraint_name;
    execute immediate vDDL;
    insert into tmp_dbobj (name, otype)
      values (c_cons.constraint_name, 'constraint');
    commit;
  end loop;
  --UK
  for c_cons in (select * from user_constraints 
                 where constraint_type in ('U') and status = 'ENABLED' 
                 ) loop
    vDDL := 'alter table '||c_cons.table_name||' disable constraint '||c_cons.constraint_name;
    execute immediate vDDL;
    insert into tmp_dbobj (name, otype)
      values (c_cons.constraint_name, 'constraint');
    commit;
  end loop;
end;

Данные об отключенных объектах сохраняются в таблицу tmp_dbobj.

Включаем, при этом, если констрейнт не включается происходит проверка, результаты которой (записи на которых констрейнт падает) сохраняются в tmp_cons_log (ВНИМАНИЕ! Тут используется функция – строковый агрегатор, отсутствующая в Oracle):

--проверка и включение констрейнтов
declare
  vSQL varchar2(1000);
  vDDL varchar2(2000);
  vCount number;
begin
  --Уникальные констрайнты
  for c1 in (select a.* 
               from tmp_dbobj a,
                    user_constraints b
               where a.otype = 'constraint'
                 and b.constraint_name = a.name 
                 and b.constraint_type = 'U') loop
    for c_cols in (select constraint_name, table_name, str_agg(column_name) cols, str_agg(column_name||' is null') null_cols
                     from user_cons_columns 
                     where constraint_name = c1.name
                     group by constraint_name, table_name) loop
      c_cols.null_cols := replace(c_cols.null_cols,',',' and ');
      vSQL := 'select 1 from dual where not exists (select '||c_cols.cols||', count(*) cnt from '||
        c_cols.table_name||
        ' where not ('||c_cols.null_cols||') group by '||c_cols.cols||' having count(*)>1 )';
      begin
        execute immediate vSQL into vCount;
        vDDL := 'alter table '||c_cols.table_name||' enable constraint '||c1.name;
        execute immediate vDDL;
        delete from tmp_dbobj where name = c1.name;
        commit;
      exception
        when no_data_found then
          vSQL := 'begin '||
                  'for c1 in ( select '||replace(c_cols.cols,',','||'',''||')||' vals from ('||
                  'select '||c_cols.cols||', count(*) cnt from '||c_cols.table_name||
                  'where not ('||c_cols.null_cols||') group by '||c_cols.cols||' having count(*)>1 )) loop'||
                  'insert into tmp_cons_log (cname, ctype, tname, err_text, cols, vals)'||
                  'values ('''||c_cols.constraint_name||''', ''U'', '''||c_cols.table_name||''', ''Дублирующие значения'', '''||
                  c_cols.cols||''', c1.vals); '||
                  'end loop; commit; end;';
          execute immediate vSQL;
      end;
    end loop;
  end loop;
  --Все остальные (FK)
  for c1 in (select a.*, b.table_name
               from tmp_dbobj a,
                    user_constraints b
               where a.otype = 'constraint'
                 and b.constraint_name = a.name 
                 and b.constraint_type = 'R') loop
    begin
      vDDL := 'alter table '||c1.table_name||' enable constraint '||c1.name;
      execute immediate vDDL;
      delete from tmp_dbobj where name = c1.name;
      commit;
    exception
      when others then
        for c_cols in (select col.constraint_name, col.table_name, r_col.table_name r_table_name,
                              fdc_str_agg(col.column_name) cols, 
                              fdc_str_agg('r.'||r_col.column_name||' = a.'||col.column_name) w_cols,
                              fdc_str_agg('a.'||col.column_name||' is not null') w_cols2
                         from user_constraints con,
                              user_cons_columns col,
                              user_cons_columns r_col
                         where con.constraint_name = c1.name
                           and col.constraint_name = con.constraint_name
                           and r_col.constraint_name = con.r_constraint_name
                           and col.position = r_col.position
                         group by col.constraint_name, col.table_name, r_col.table_name) loop
          vSQL := 'begin '||
                  'for c2 in ( select '||replace(c_cols.cols,',','||'',''||')||' vals from '||
                  c_cols.table_name||' a '||
                  'where not exists (select 1 from '||c_cols.r_table_name||' r '||
                  'where '||replace(c_cols.w_cols,',',' and ')||')'||
                  'and '||replace(c_cols.w_cols2,',',' and ')||
                  ') loop '||
                  'insert into tmp_cons_log (cname, ctype, tname, err_text, cols, vals)'||
                  'values ('''||c_cols.constraint_name||''', ''R'', '''||c_cols.table_name||''', ''Нет родительской записи'', '''||
                  c_cols.cols||''', c2.vals); '||
                  'end loop; commit; end;';
          dbms_output.put_line(vSQL);
          execute immediate vSQL;
        end loop;
    end;
  end loop;
  --триггера
  for c1 in (select * from tmp_dbobj where otype = 'trigger') loop
    vDDL := 'alter trigger '||c1.name||' enable';
    execute immediate vDDL;
    delete from tmp_dbobj where name = c1.name;
    commit;
  end loop;  
end;
Рубрика: Oracle | Оставить комментарий