我的问题
Oracle'DATE'列实际上也存储时间,精度低于'TIMESTAMP'(秒与皮秒)。我需要我的应用程序与此旧架构进行交互,就像Date是DateTime一样。因为rails将此字段视为日期,所以它会截断时间。
示例:
2.4.1 :003 > m.send_after_ts = Time.now
=> 2018-03-15 11:45:50 -0600
2.4.1 :004 > m.send_after_ts
=> Thu, 15 Mar 2018
配置数据:
#columns
的结果:
#<ActiveRecord::ConnectionAdapters::OracleEnhancedColumn:0x00000005501658
@collation=nil,
@comment=nil,
@default=nil,
@default_function=nil,
@name="send_after_ts",
@null=true,
@object_type=false,
@returning_id=false,
@sql_type_metadata=
#<ActiveRecord::ConnectionAdapters::SqlTypeMetadata:0x00000005501720
@limit=nil,
@precision=nil,
@scale=nil,
@sql_type="DATE",
@type=:date>,
@table_name="mail",
@virtual=false>,
版本:
ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin17]
Rails 5.1.5
activerecord (5.1.5)
activerecord-oracle_enhanced-adapter (1.8.2)
ruby-oci8 (2.2.5.1)
我认为某个地方必须有一个映射来管理这种关系?如何让rails将此字段转换为时间戳?
更新
将attribute :send_after_ts, :datetime
添加到我的模型允许rails将该字段视为DateTime,但在尝试写入db时会导致异常:
SQL (4.4ms) INSERT INTO "MAIL" ("SEND_AFTER_TS", "ID") VALUES (:a1, :a2) [["send_after_ts", "2018-03-15 12:58:02.635810"], ["id", 6778767]]
ActiveRecord::StatementInvalid: OCIError: ORA-01830: date format picture ends before converting entire input string: INSERT INTO "MAIL" ("SEND_AFTER_TS", "ID") VALUES (:a1, :a2)
from (irb):3
我认为这是由额外的精度(小数秒)引起的,但我没有办法将其定义为属性设置的一部分。
我现在可以通过将此字段写为字符串来解决此问题,例如:
2.4.1 :013 > m.send_after_ts = Time.now.to_s
=> "Mar 15, 2018 12:48"
2.4.1 :014 > m.save
=> true
我仍在寻找真正的解决方案。
答案 0 :(得分:1)
您可以使用虚拟属性模式作为界面:
def send_after_ts_datetime
send_after_ts.to_datetime
end
def send_after_ts_datetime=(datetime)
self.send_after_ts = datetime.to_s
end
现在,您将使用_datetime
方法读取和写入属性,但您的数据将存储在原始send_after_ts
列中。
>> m.send_after_ts_datetime = Time.now
#> "2018-03-15 15:15:49 -0600"
>> m.send_after_ts_datetime
#> Thu, 15 Mar 2018 15:15:49 -0600
答案 1 :(得分:0)
一个老问题,但是我今天只需要自己解决这个问题,所以我想分享一下我的hacky解决方案。
OracleEnhancedAdapter
扩展了ActiveRecord::ConnectionAdapters::AbstractAdapter
。在初始化期间,它将调用其initialize_type_map
方法来映射这些类型。 OracleEnhancedAdapter包含对此的覆盖,后者依次在AbstractAdapter
中调用超类方法。在这里将不需要的日期类型映射到oracle DATE类型:
register_class_with_precision m, %r(date)i, Type::Date
为此,我在rails中创建了一个覆盖此方法的初始化程序:
ActiveSupport.on_load(:active_record) do
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
# ... some other method customization
def initialize_type_map(m)
# other code from the AbstractAdapter implementation
# actually make the mapping to the DateTime that you really want
register_class_with_precision m, %r(date)i, ActiveRecord::Type::DateTime
# ... code from the OracleEnhancedAdapter implementation
end
end
end
结果是,oracle DATE类型现在在rails中被视为日期时间。
答案 2 :(得分:0)
将此代码添加到您的代码中以强制Oracle“ DATE”使用DateTime红宝石类型
require 'active_record/connection_adapters/oracle_enhanced_adapter'
module ActiveRecord
module ConnectionAdapters
class OracleEnhancedAdapter
alias :old_initialize_type_map :initialize_type_map
def initialize_type_map(m = type_map)
old_initialize_type_map(m)
m.register_type "DATE", Type::DateTime.new
end
end
end
end