Rails tips (1) dependent 设置问题

 

has_many has_one belongs_to都有设置dependent属性

 

has_many的文档里写的dependent是设置为:destroy将调用关联对象destroy方法,如果设置为:delete_all将调用class.delete_all而不调用destroy方法,:nullify就是直接将外键设为null.

 

has_one的文档里写的dependent是设置为:destroy将调用关联对象destroy方法,如果设置为:delete将调用class.delete而不调用destroy方法,:nullify就是直接将外键设为null.

 

belongs_to的文档里写的dependent是设置为:destroy将调用关联对象destroy方法,如果设置为:delete将deleted而不调用destroy方法.

 

 

 

继续查源代码了解清楚一点:

 

1) has_many的源代码

 

def has_many(association_id, options = {}, &extension)
        reflection = create_has_many_reflection(association_id, options, &extension)

        configure_dependency_for_has_many(reflection)
        ...
      end
 

 

        # delete children, otherwise foreign key is set to NULL.
        def configure_dependency_for_has_many(reflection)
          if reflection.options.include?(:dependent)
            # Add polymorphic type if the :as option is present
            dependent_conditions = []
            dependent_conditions << "#{reflection.primary_key_name} = \#{record.quoted_id}"
            dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as]
            dependent_conditions << sanitize_sql(reflection.options[:conditions]) if reflection.options[:conditions]
            dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")

            case reflection.options[:dependent]
              when :destroy # 一个个用destroy删除
                method_name = "has_many_dependent_destroy_for_#{reflection.name}".to_sym
                define_method(method_name) do
                  send("#{reflection.name}").each { |o| o.destroy }
                end
                before_destroy method_name
              when :delete_all # 用delete_all指令删除,hook proc到before_destroy
                module_eval "before_destroy { |record| #{reflection.class_name}.delete_all(%(#{dependent_conditions})) }"
              when :nullify # 用update_all指令更新为null,hook proc到before_destroy
                module_eval "before_destroy { |record| #{reflection.class_name}.update_all(%(#{reflection.primary_key_name} = NULL),  %(#{dependent_conditions})) }"
              else
                raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})"
            end
          end
        end

 

如果只是简单的关联,has_many的delete_all方法会比destroy效率高不少,destroy是一个n+1查询。

实践证明其实对于大表的删除,最好不使用dependent属性,性能问题迟早会找上来 

 

2) has_one的源代码:

 

def has_one(association_id, options = {})
    ...
    configure_dependency_for_has_one(reflection)
end
 

 

        def configure_dependency_for_has_one(reflection)
          if reflection.options.include?(:dependent)
            case reflection.options[:dependent]
              when :destroy # 创建destroy关联方法,hook到before_destroy 
                method_name = "has_one_dependent_destroy_for_#{reflection.name}".to_sym
                define_method(method_name) do
                  association = send("#{reflection.name}")
                  association.destroy unless association.nil?
                end
                before_destroy method_name
              when :delete # 创建delete关联方法,调用类的delete(id)方法,hook到before_destroy 
                method_name = "has_one_dependent_delete_for_#{reflection.name}".to_sym
                define_method(method_name) do
                  association = send("#{reflection.name}")
                  association.class.delete(association.id) unless association.nil?
                end
                before_destroy method_name
              when :nullify # 调用record.update(attr,nil),hook到before_destroy 
                method_name = "has_one_dependent_nullify_for_#{reflection.name}".to_sym
                define_method(method_name) do
                  association = send("#{reflection.name}")
                  association.update_attribute("#{reflection.primary_key_name}", nil) unless association.nil?
                end
                before_destroy method_name
              else
                raise ArgumentError, "The :dependent option expects either :destroy, :delete or :nullify (#{reflection.options[:dependent].inspect})"
            end
          end
        end

 

3) belongs_to相关源代码:

 

def belongs_to(association_id, options = {})
    ...
    configure_dependency_for_belongs_to(reflection)
end

 

 

        def configure_dependency_for_belongs_to(reflection)
          if reflection.options.include?(:dependent)
            case reflection.options[:dependent]
              when :destroy # 直接调用destroy删除,hook到before_destroy 
                method_name = "belongs_to_dependent_destroy_for_#{reflection.name}".to_sym
                define_method(method_name) do
                  association = send("#{reflection.name}")
                  association.destroy unless association.nil?
                end
                before_destroy method_name
              when :delete #调用类方法的delete(id)删除,hook到before_destroy
                method_name = "belongs_to_dependent_delete_for_#{reflection.name}".to_sym
                define_method(method_name) do
                  association = send("#{reflection.name}")
                  association.class.delete(association.id) unless association.nil?
                end
                before_destroy method_name
              else
                raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{reflection.options[:dependent].inspect})"
            end
          end
        end

 

 

你可能感兴趣的:(sql,Rails)