saveしているはずなのにsaveできないんです!

ruby 1.9.2p290 (2011-07-09) [i386-mingw32]
activerecord (3.1.0)
activerecord-oracle_enhanced-adapter (1.4.2)

Oracle 11g XEのHRスキーマ

SQL> select employee_id, first_name from employees where employee_id = 100;

EMPLOYEE_ID FIRST_NAME
----------- ----------------------------------------
        100 Stevennn
emp = Employee.find(100)
p emp.first_name # => Stevennn
emp.first_name.chop!
emp.first_name.chop!
emp.save!
p emp.first_name # => Steven

sqlplusで確認してみる。

SQL> select employee_id, first_name from employees where employee_id = 100;

EMPLOYEE_ID FIRST_NAME
----------- ----------------------------------------
        100 Stevennn

なんでや。。なんべんやってもスチーブンンンさんのままや。。ログはどうなってるかというと

D, [2013-06-19T01:33:09.631647 #4880] DEBUG -- :   employees Columns (2.0ms)  SELECT column_name AS name, data_type AS sql_type, data_default, nullable, virtual_column, hidden_column, DECODE(data_type, 'NUMBER', data_precision, 'FLOAT', data_precision, 'VARCHAR2', DECODE(char_used, 'C', char_length, data_length), 'RAW', DECODE(char_used, 'C', char_length, data_length), 'CHAR', DECODE(char_used, 'C', char_length, data_length), NULL) AS limit, DECODE(data_type, 'NUMBER', data_scale, NULL) AS scale FROM all_tab_cols WHERE owner = 'HR' AND table_name = 'EMPLOYEES' AND hidden_column = 'NO' ORDER BY column_id
D, [2013-06-19T01:33:09.654649 #4880] DEBUG -- :    (13.0ms)  SELECT DECODE(table_name, UPPER(table_name), LOWER(table_name), table_name) FROM all_tables WHERE owner = SYS_CONTEXT('userenv', 'session_user') AND secondary = 'N'
D, [2013-06-19T01:33:09.657649 #4880] DEBUG -- :   Primary Key (2.0ms)  SELECT cc.column_name FROM all_constraints c, all_cons_columns cc WHERE c.owner = 'HR' AND c.table_name = 'EMPLOYEES' AND c.constraint_type = 'P' AND cc.owner = c.owner AND cc.constraint_name = c.constraint_name
D, [2013-06-19T01:33:09.660649 #4880] DEBUG -- :   Primary Key (2.0ms)  SELECT cc.column_name FROM all_constraints c, all_cons_columns cc WHERE c.owner = 'HR' AND c.table_name = 'EMPLOYEES' AND c.constraint_type = 'P' AND cc.owner = c.owner AND cc.constraint_name = c.constraint_name
D, [2013-06-19T01:33:09.745654 #4880] DEBUG -- :   Employee Load (84.0ms)  SELECT "EMPLOYEES".* FROM "EMPLOYEES" WHERE "EMPLOYEES"."EMPLOYEE_ID" = :a1 AND ROWNUM <= 1  [["employee_id", 100]]

全然updateなんかしていません。なんでや。。

オーソドックスにinsert -> updateしてみる。

emp = Employee.new(
  last_name: 'tanaka',
  email: 'tanakataro@hoge.com',
  hire_date: Time.local(1999,11,22),
  job_id: 'ST_CLERK')
emp.save!

emp2 = Employee.find(emp.id)
emp2.first_name = 'taro'
emp2.save!

ログが

D, [2013-06-19T01:36:53.872473 #7132] DEBUG -- :   employees Columns (3.0ms)  SELECT column_name AS name, data_type AS sql_type, data_default, nullable, virtual_column, hidden_column, DECODE(data_type, 'NUMBER', data_precision, 'FLOAT', data_precision, 'VARCHAR2', DECODE(char_used, 'C', char_length, data_length), 'RAW', DECODE(char_used, 'C', char_length, data_length), 'CHAR', DECODE(char_used, 'C', char_length, data_length), NULL) AS limit, DECODE(data_type, 'NUMBER', data_scale, NULL) AS scale FROM all_tab_cols WHERE owner = 'HR' AND table_name = 'EMPLOYEES' AND hidden_column = 'NO' ORDER BY column_id
D, [2013-06-19T01:36:53.896475 #7132] DEBUG -- :    (13.0ms)  SELECT DECODE(table_name, UPPER(table_name), LOWER(table_name), table_name) FROM all_tables WHERE owner = SYS_CONTEXT('userenv', 'session_user') AND secondary = 'N'
D, [2013-06-19T01:36:53.898475 #7132] DEBUG -- :   Primary Key (2.0ms)  SELECT cc.column_name FROM all_constraints c, all_cons_columns cc WHERE c.owner = 'HR' AND c.table_name = 'EMPLOYEES' AND c.constraint_type = 'P' AND cc.owner = c.owner AND cc.constraint_name = c.constraint_name
D, [2013-06-19T01:36:53.902475 #7132] DEBUG -- :   Primary Key (2.0ms)  SELECT cc.column_name FROM all_constraints c, all_cons_columns cc WHERE c.owner = 'HR' AND c.table_name = 'EMPLOYEES' AND c.constraint_type = 'P' AND cc.owner = c.owner AND cc.constraint_name = c.constraint_name
D, [2013-06-19T01:36:53.913476 #7132] DEBUG -- :   Primary Key (2.0ms)  SELECT cc.column_name FROM all_constraints c, all_cons_columns cc WHERE c.owner = 'HR' AND c.table_name = 'EMPLOYEES' AND c.constraint_type = 'P' AND cc.owner = c.owner AND cc.constraint_name = c.constraint_name
D, [2013-06-19T01:36:53.914476 #7132] DEBUG -- :   Primary Key Trigger (1.0ms)   SELECT trigger_name
 FROM all_triggers
 WHERE owner = 'HR'
 AND trigger_name = 'EMPLOYEES_PKT'
 AND table_owner = 'HR'
 AND table_name = 'EMPLOYEES'
 AND status = 'ENABLED'

D, [2013-06-19T01:36:54.016482 #7132] DEBUG -- :   SQL (100.0ms)  INSERT INTO "EMPLOYEES" ("COMMISSION_PCT", "DEPARTMENT_ID", "EMAIL", "EMPLOYEE_ID", "FIRST_NAME", "HIRE_DATE", "JOB_ID", "LAST_NAME", "MANAGER_ID", "PHONE_NUMBER", "SALARY") VALUES (:a1, :a2, :a3, :a4, :a5, :a6, :a7, :a8, :a9, :a10, :a11)  [["commission_pct", nil], ["department_id", nil], ["email", "tanakataro@hoge.com"], ["employee_id", 209], ["first_name", nil], ["hire_date", 1999-11-22 00:00:00 +0900], ["job_id", "ST_CLERK"], ["last_name", "tanaka"], ["manager_id", nil], ["phone_number", nil], ["salary", nil]]
D, [2013-06-19T01:36:54.019482 #7132] DEBUG -- :   Employee Load (1.0ms)  SELECT "EMPLOYEES".* FROM "EMPLOYEES" WHERE "EMPLOYEES"."EMPLOYEE_ID" = :a1 AND ROWNUM <= 1  [["employee_id", 209]]
D, [2013-06-19T01:36:54.021482 #7132] DEBUG -- :    (0.0ms)  UPDATE "EMPLOYEES" SET "FIRST_NAME" = 'taro' WHERE "EMPLOYEES"."EMPLOYEE_ID" = 209

ちゃんとinsertとupdateが実行されている。sqlplusでも確認できる。

SQL> select * from employees where employee_id = (select max(employee_id) from employees);

EMPLOYEE_ID FIRST_NAME LAST_NAME
----------- ---------- ---------
        209 taro       tanaka

だから書き方が悪いんだな。

たしかモデルの変更有無を調べるchange?みたいなメソッドなかったっけ。多分アクセサメソッド経由でないと反映されなくて、first_nameを破壊的メソッド(chop!)で変更してしまったから

ActiveRecord「ご主人様、updateの必要はございませんので無視しておきました」

とかいうことになったんだろうな。

emp = Employee.find(100)
emp.first_name.chop!
emp.first_name.chop!
p emp.changed?            # => false
p emp.changes             # => {}
p emp.first_name_change   # => nil

ほらね。破壊的メソッドで編集してはだめです。

(;^ω^)ほひー