简体   繁体   English

在带有 PreparedStatement 的 Insert 语句中使用多个值的更有效方法是什么?

[英]What's a more efficient way to use multiple values in Insert statement w/ PreparedStatement?

Basically, how can I change the following so I don't have multiple question marks?:基本上,我怎样才能改变以下内容,这样我就没有多个问号了?:


public static int insert(int id, String name, String address, String phone, int age) throws SQLException {

        String SQL = "INSERT INTO some_table (ID, Name, Address, Phone_Number, Age) VALUES (?, ?, ?, ?, ?)";

        PreparedStatement ps = jdbc.connection.prepareStatement(SQL);
        ps.setInt(1, id);
        ps.setString(2, name);
        ps.setString(3, address);
        ps.setString(4, phone);
        ps.setInt(5, age);

        return ps.executeUpdate();
    }

Efficiency is a rather nebulous word.效率是一个相当模糊的词。 It sounds like you mean it in the sense of 'I find this a rather clumsy way to write, surely there is a way to write an insert statement that takes less effort and is easier to read - that is more efficient from the point of view of writing/reading/maintaining the code.听起来您的意思是“我发现这是一种相当笨拙的编写方式,当然有一种方法可以编写插入语句,该语句花费更少的精力并且更易于阅读-从观点来看效率更高编写/阅读/维护代码。 Let's call that the 'code efficiency' factor.我们称之为“代码效率”因素。

The other obvious meaning of 'efficient' is how long it takes from the top of this method to the end of it, let's call that db efficiency. “高效”的另一个明显含义是从该方法的顶部到结束需要多长时间,我们称之为 db 效率。

Code efficiency代码效率

Sure, there are vastly better ways than this.当然,还有比这更好的方法。 JDBC is intentional 'low level glue' - a ton of libraries are built on top of it, which means extending it or changing it is complicated (java is not, as a rule, in the business of making all existing code and libraries broken by making breaking changes). JDBC 是有意的“低级胶水”——大量的库建立在它之上,这意味着扩展它或改变它很复杂(通常,java 不是在使所有现有代码和库被破坏的业务中进行重大更改)。 The JDBC API needs to expose every feature a database could plausibly want to expose, even features that are rarely needed. JDBC API 需要公开数据库可能想要公开的所有功能,甚至是很少需要的功能。

As a consequence, you should not use raw JDBC when interacting with the DB directly .因此,直接与数据库交互时不应使用原始 JDBC It's glue - stuff that DB abstraction layers use.它是胶水——数据库抽象层使用的东西。

JDBI and JOOQ JDBI 和 JOOQ

Instead, use libraries like JOOQ( https://jooq.org ) or JDBI .相反,使用像 JOOQ( https://jooq.org ) 或JDBI 这样的库。

These libraries offer various options.这些图书馆提供各种选择。 Here are some JDBI samples:以下是一些 JDBI 示例:


public interface UserDao {
  @SqlUpdate("INSERT INTO \"user\" (id, \"name\") VALUES (?, ?)")
  void insert(int id, String name);
}

// .... and to use:

jdbi.withExtension(UserDao.class, dao -> {
  dao.insert(0, "Alice");
});

Note that if you want the safest, easiest to use transaction level (SERIALIZABLE), all your access to the DB needs to be in lambda form, because manually handling the retry is silly, don't do that.请注意,如果您想要最安全、最容易使用的事务级别 (SERIALIZABLE),您对数据库的所有访问都需要采用 lambda 形式,因为手动处理重试很愚蠢,不要那样做。 Yet another excellent example why using JDBC directly is a very bad idea.另一个很好的例子为什么直接使用 JDBC 是一个非常糟糕的主意。

What's retry?什么叫重试? Long story - see the footnote 1 .长话短说——见脚注1

Focus on the insert part - that's easier here.专注于插入部分 - 这里更容易。 You just.. call a method, passing the data.你只是..调用一个方法,传递数据。

Or, even simpler if you're doing a one-off insert (as in, only one spot in the code would ever do it, not 'I only insert one record'):或者,如果您正在执行一次性插入,则更简单(例如,代码中只有一个位置会执行此操作,而不是“我只插入一条记录”):

jdbi.withHandle(handle -> {
  handle.execute("INSERT INTO \"user\" (id, \"name\") VALUES (?, ?)", 0, "Alice");
});

Table mapping表映射

A third option is to define classes in java and then ask a DB abstraction engine to 'convert' these into CREATE TABLE statements.第三种选择是在 java 中定义类,然后要求数据库抽象引擎将它们“转换”为CREATE TABLE语句。 Then inserting a new row in the table involves making an instance of that class and saving it.然后在表中插入一个新行涉及创建该 class 的实例并保存它。 Hibernate is the go-to tool for this. Hibernate是这方面的首选工具。 Hibernate is best treated as a system that saves and retrieves java objects that has nothing to do with SQL, the fact that it works by saving things to a DB should be treated as an implementation detail. Hibernate 最好被视为一个保存和检索 java 对象的系统,与 SQL 无关,它通过将东西保存到数据库来工作的事实应该被视为一个实现细节。 Using hibernate can be very convenient indeed but expect high learning curves if you need to tweak or optimize the queries it ends up running.使用 hibernate 确实非常方便,但如果您需要调整或优化它最终运行的查询,则需要很高的学习曲线。

No, only use JDBC不,只使用 JDBC

This boils down to 'Badly reinvent JOOQ or JDBI yourself'.这归结为“自己严重改造 JOOQ 或 JDBI”。 Make a method that takes the 5 args as argument and runs an insert, and consider turning that method into a Builder if you want a prettier API that is a lot easier to read later on when you forgot the order in which the arguments go.创建一个将 5 个参数作为参数并运行插入的方法,如果您想要更漂亮的 API,当您忘记 arguments go 的顺序时,它更容易阅读,请考虑将该方法转换为生成器

DB efficiency数据库效率

Databases will have plenty of docs on how to do fast inserts.数据库将有大量关于如何进行快速插入的文档。 A few highlights:几个亮点:

  • If you're doing a ton of inserts, consider using COPY instead.如果您要进行大量插入,请考虑改用COPY
  • Generally, delete all indices and triggers, insert it all, then add them afterwards.通常,删除所有索引和触发器,将其全部插入,然后再添加。 Or, disable indices and triggers, add everything, then re-enable them and check them.或者,禁用索引和触发器,添加所有内容,然后重新启用它们并检查它们。 How to do that?怎么做? Depends on your DB.取决于您的数据库。 These steps would mean you can't have a 'running system' (if other code is querying while you are doing this, you can't just turn the triggers off).这些步骤将意味着您不能拥有“正在运行的系统”(如果在您执行此操作时其他代码正在查询,则您不能只关闭触发器)。
  • If you have to go with one-by-one inserts, re-use the prepared statement, and batch commits: You don't want to commit after every insert (so, turn off auto-commit mode,).如果您必须 go 进行一对一的插入,请重新使用准备好的语句并批量提交:您不想在每次插入后都提交(因此,请关闭自动提交模式)。 but you also don't want to never commit.但你也不想永远不承诺。 Maintain a counter and commit every ~1000 inserts or so.维护一个计数器并提交每大约 1000 次插入。

[1] To truly get perfect transactions, you want any given sequence of DB actions to act as if it was the only thing on the pl.net that happened, and that it all happened in one go. This is extremely complicated and to do it truly right, the DB pretty much needs to lock everything the moment you begin. [1] 为了真正获得完美的交易,您希望任何给定的数据库操作序列都表现得好像它是 pl.net 上唯一发生的事情,并且这一切都发生在一个 go 中。这是非常复杂的,需要做确实如此,数据库几乎需要在您开始时锁定所有内容 Which would mean a serious, multi-core system is going to run slow as molasses, so DBs instead double check once you commit if it could have been done like that - basically, it checks if every query you made during the transaction would still return the same results.这意味着一个严肃的多核系统将像糖蜜一样运行缓慢,因此数据库会在您提交后仔细检查是否可以这样做 - 基本上,它会检查您在交易期间进行的每个查询是否仍会返回同样的结果。 If yes, the commit happens.如果是,则提交发生。 If not, the commit fails - with a 'retry error'.如果不是,则提交失败 - 并出现“重试错误”。 Which you handle by... doing the transaction again.您通过...再次进行交易来处理。 However, computers tend to be annoyingly reliable, so you run the risk of the computer equivalent of you almost smacking into someone on the street, so you lean left, but they lean right, and it's like a slapstick routine.然而,计算机往往可靠得令人讨厌,所以你冒着计算机相当于你在街上差点撞到某人的风险,所以你向左倾斜,但他们向右倾斜,这就像一个闹剧套路。 To avoid that , you wait increasingly random amounts of times.为避免这种情况,您等待的次数越来越随机。 This is all quite complicated (both handling the retry, as you need some DB-engine specific trickery to detect these, as well as doing the randomized incremental backoff) so you definitely don't want to write it yourself.这一切都非常复杂(既要处理重试,因为您需要一些特定于 DB 引擎的技巧来检测这些,还要进行随机增量退避),因此您绝对不想自己编写。 By passing a lambda (the thing with the array), JDBI/JOOQ can do it for you.通过传递 lambda(带有数组的东西),JDBI/JOOQ 可以为您完成。

Each parameter placeholder ( ? ) takes the place of one scalar value in your SQL. You need one parameter for each scalar value.每个参数占位符 ( ? ) 代替 SQL 中的一个标量值。每个标量值都需要一个参数。

There's no such thing as binding an array or a collection to one parameter.没有将数组或集合绑定到一个参数这样的事情。 Some frameworks may have a convenience method that takes a collection and rewrites your SQL so it has one parameter placeholder for each element in the collection.一些框架可能有一个方便的方法,它接受一个集合并重写你的 SQL 所以它有一个参数占位符用于集合中的每个元素。 But as far as SQL is concerned, it never sees that.但就 SQL 而言,它永远看不到。

It's not inefficient in terms of runtime performance, even though it takes a few lines of code.它在运行时性能方面并不低效,即使它需要几行代码。 But that's what you get paid to do, so go for it.但这就是你得到报酬的事情,所以 go。 :-) :-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM