如何生成通用sql对象?

pobjuy32  于 2021-07-26  发布在  Java
关注(0)|答案(1)|浏览(317)

我正在用ruby制作一个模拟orm,存储“论坛”数据。
我有以下表格: users (id, fname, lname) ,和 questions (id, title, body, author_id) . a_user.create 以及 a_question.create 大致相同:


# aUser#create

    def create
        # adds a user to the database
        raise "user already in db" if self.id

        begin
            QuestionsDB.instance.execute(<<-SQL, self.fname, self.lname)
                insert into users (fname, lname)
                values (?, ?);
            SQL
            self.id = QuestionsDB.instance.last_insert_row_id

            return self.id
        rescue => exception
            raise "failed to create user: #{exception}"
        end
    end

# aQuestion#create

    def create
        # add question to db
        raise "already asked" if self.id
        raise "title already exists" unless Question.find_by_title(self.title).empty?

        begin
            QuestionsDB.instance.execute(<<-SQL, self.title, self.body, self.author_id)
                insert into questions (title, body, author_id)
                values (?, ?, ?);
            SQL
            self.id = QuestionsDB.instance.last_insert_row_id

            return self.id
        rescue => exception
            raise "failed to create question: #{exception}"
        end
    end

我正在写另一个类modelbase,我想说

ModelBase.create(aUser)

aMB = ModelBase.new(~characteristics_from_user_or_question_object~)
aMB.create

. 用户和问题只有在添加到数据库后才具有id。
两个creates的sql基本上都接受所有预创建的属性,并将它们放到相应表中的所有列中,不包括第一个id列。有没有一种方法可以概括“将此对象的所有属性插入指定的值表('all but the first')”的指令?

rfbsl7qr

rfbsl7qr1#

我们可以用一些ruby元编程来实现一个非常基本的orm,但是请记住,出于安全原因,在生产中不应该使用这个。实现一个完全工作的orm是一项艰巨的工作。
我们可以用 instance_variables .

class User
  attr_reader :id, :first_name, :last_name

  def initialize(first_name:, last_name:)
    @first_name = first_name
    @last_name = last_name
  end
end

puts User.new(first_name: "Elvis", last_name: "Presley").instance_variables

# @first_name @last_name

请注意 @ 一开始我们可以用一个简单的 gsub . 我们还需要拒绝 @id 属性。最后与 instance_variable_get 我们可以得到实际值。
有了这些信息,我们现在可以实现 columns 以及 values 方法。

class OrmBase
  def columns
    instance_variables_without_id.map do |var_name|
      var_name.to_s.gsub /^@/, ''
    end
  end

  def values
    instance_variables_without_id.map do |var_name|
      instance_variable_get var_name
    end
  end

  def instance_variables_without_id
    instance_variables.reject { |var_name| var_name == "@id" }
  end
end

现在我们需要计算表名,我们可以从具有 self.class.name . 如果我们把所有的东西都放在一起,会像这样:

class OrmBase
  def create
    return if @id

    QuestionsDB.instance.execute(to_sql)
    @id = QuestionsDB.instance.last_insert_row_id
    self
  end

  def to_sql
    "INSERT INTO #{table_name} (#{columns.join(', ')}) values (#{values.join(', ')});" 
  end

  private

  def table_name
    "#{self.class.name}s".downcase  
  end

  def columns
    instance_variables_without_id.map do |var_name|
      var_name.to_s.gsub(/^@/, '')
    end
  end

  def values
    instance_variables_without_id.map do |var_name|
      instance_variable_get(var_name)
    end
  end

  def instance_variables_without_id
    instance_variables.reject { |var_name| var_name == "@id" }
  end
end

class User < OrmBase
  attr_reader :id, :first_name, :last_name

  def initialize(first_name:, last_name:)
    @first_name = first_name
    @last_name = last_name
  end
end

class Question < OrmBase
  attr_reader :id, :title

  def initialize(title:)
    @title = title
  end
end

puts User.new(first_name: "Elvis", last_name: "Presley").to_sql

# INSERT INTO users (first_name, last_name) values (Elvis, Presley);

puts Question.new(title: "What is your favorite song?").to_sql

# INSERT INTO questions (title) values (What is your favorite song?);

如开头所述,请仅将此作为练习的一部分使用,因为它不安全(sql注入)、存在错误(例如表名的多元化和值的转义)等。
另一种实现方法是,首先查询数据库表具有哪些列名,然后从中派生类属性。比如activerecord就是这样做的。

相关问题