可用版本: Dev (3.21) | 最新版 (3.20) | 3.19 | 3.18 | 3.17 | 3.16 | 3.15 | 3.14 | 3.13 | 3.12 | 3.11

POJO

适用于 ✅ 开源版   ✅ 专业版   ✅ 企业版

只要您的应用程序不是真正的分层,或者只要您仍然在 DAO 层中编写代码,以记录形式获取数据是可以的。但是,如果您有一个更高级的应用程序架构,您可能不希望 jOOQ 工件泄漏到其他层中。您可以选择编写 POJO(Plain Old Java Objects)作为您的主要 DTO(数据传输对象),而不依赖于 jOOQ 的 org.jooq.Record 类型,这些类型甚至可能持有对 Configuration 的引用,从而持有 JDBC java.sql.Connection。像 Hibernate/JPA 一样,jOOQ 允许您使用 POJO 进行操作。与 Hibernate/JPA 不同,jOOQ 不会“附加”这些 POJO 或使用任何魔法创建代理。

如果您正在使用 jOOQ 的 代码生成器,您可以配置它来为您生成 POJO,但您不需要使用这些生成的 POJO。您可以使用自己的。请参阅手册中关于带有自定义 RecordMapper 的 POJO部分,了解如何修改 jOOQ 的标准 POJO 映射行为。

虽然 POJO 没有与 jOOQ API 的连接,但它们也没有脏标志和其他有用的基于记录的功能的概念。它们是哑 POJO。虽然有些人喜欢“干净”的关注点分离,但这并不总是必要的。在 UI 层中,大部分时间使用 jOOQ 的 org.jooq.UpdatableRecord 也是完全可以的!

equals() 和 hashCode()

生成的 POJO 默认提供完全基于值的 equals()hashCode() 语义,因为 jOOQ 以类似于 SQL 的方式操作数据,当行是“NOT DISTINCT”时,它们是“相等”的。自定义构建的 POJO 或 DTO 可以对 equals()hashCode() 的实现有不同的看法,包括 POJO 更类似于 JPA 实体的方法(如果存在主键:仅使用该主键进行比较,如果不存在,则所有实例都不同)。 jOOQ 不会强制采用一种方法来替代另一种方法。

使用 JPA 注解的 POJO

jOOQ 尝试在您的 POJO 类型上查找 JPA 注解。如果找到任何注解,它们将用作映射元信息的主要来源。 jOOQ 仅使用和理解 jakarta.persistence.Column 注解。一个例子

// A JPA-annotated POJO class
public class MyBook {
  @Column(name = "ID")
  public int myId;

  @Column(name = "TITLE")
  public String myTitle;
}

// The various "into()" methods allow for fetching records into your custom POJOs:
MyBook myBook        = create.select().from(BOOK).fetchAny().into(MyBook.class);
List<MyBook> myBooks = create.select().from(BOOK).fetch().into(MyBook.class);
List<MyBook> myBooks = create.select().from(BOOK).fetchInto(MyBook.class);

与任何其他 JPA 实现一样,您可以将 jakarta.persistence.Column 注解放在任何类成员上,包括属性、setter 和 getter。有关更多详细信息,请参阅 Record.into() Javadoc。

使用简单的 POJO

如果 jOOQ 没有找到任何 JPA 注解,则列将映射到“最佳匹配”构造函数、属性或 setter。一个例子说明了这一点

// A "mutable" POJO class
public class MyBook1 {
  public int id;
  public String title;
}

// The various "into()" methods allow for fetching records into your custom POJOs:
MyBook1 myBook        = create.select().from(BOOK).fetchAny().into(MyBook1.class);
List<MyBook1> myBooks = create.select().from(BOOK).fetch().into(MyBook1.class);
List<MyBook1> myBooks = create.select().from(BOOK).fetchInto(MyBook1.class);

有关更多详细信息,请参阅 Record.into() Javadoc。

使用“不可变”的 POJO

如果 jOOQ 没有找到任何默认构造函数,则列将映射到“最佳匹配”构造函数。这允许使用 jOOQ 的“不可变” POJO。一个例子说明了这一点

// An "immutable" POJO class
public class MyBook2 {
  public final int id;
  public final String title;

  public MyBook2(int id, String title) {
    this.id = id;
    this.title = title;
  }
}

// With "immutable" POJO classes, there must be an exact match between projected fields and available constructors:
MyBook2 myBook        = create.select(BOOK.ID, BOOK.TITLE).from(BOOK).fetchAny().into(MyBook2.class);
List<MyBook2> myBooks = create.select(BOOK.ID, BOOK.TITLE).from(BOOK).fetch().into(MyBook2.class);
List<MyBook2> myBooks = create.select(BOOK.ID, BOOK.TITLE).from(BOOK).fetchInto(MyBook2.class);

// An "immutable" POJO class with a java.beans.ConstructorProperties annotation
public class MyBook3 {
  public final String title;
  public final int id;

  @ConstructorProperties({ "title", "id" })
  public MyBook3(String title, int id) {
    this.title = title;
    this.id = id;
  }
}

// With annotated "immutable" POJO classes, there doesn't need to be an exact match between fields and constructor arguments.
// In the below cases, only BOOK.ID is really set onto the POJO, BOOK.TITLE remains null and BOOK.AUTHOR_ID is ignored
MyBook3 myBook        = create.select(BOOK.ID, BOOK.AUTHOR_ID).from(BOOK).fetchAny().into(MyBook3.class);
List<MyBook3> myBooks = create.select(BOOK.ID, BOOK.AUTHOR_ID).from(BOOK).fetch().into(MyBook3.class);
List<MyBook3> myBooks = create.select(BOOK.ID, BOOK.AUTHOR_ID).from(BOOK).fetchInto(MyBook3.class);

有关更多详细信息,请参阅 Record.into() Javadoc。

使用可代理类型

jOOQ 还允许将数据提取到抽象类或接口中,或者换句话说,提取到“可代理”类型中。这意味着 jOOQ 将返回一个包装在 java.lang.reflect.Proxy 中的 java.util.HashMap,该代理实现您的自定义类型。这里给出了一个例子

// A "proxyable" type
public interface MyBook3 {
  int getId();
  void setId(int id);

  String getTitle();
  void setTitle(String title);
}

// The various "into()" methods allow for fetching records into your custom POJOs:
MyBook3 myBook        = create.select(BOOK.ID, BOOK.TITLE).from(BOOK).fetchAny().into(MyBook3.class);
List<MyBook3> myBooks = create.select(BOOK.ID, BOOK.TITLE).from(BOOK).fetch().into(MyBook3.class);
List<MyBook3> myBooks = create.select(BOOK.ID, BOOK.TITLE).from(BOOK).fetchInto(MyBook3.class);

有关更多详细信息,请参阅 Record.into() Javadoc。

将 POJO 加载回记录以存储它们

以上示例显示了如何将数据提取到您自己的自定义 POJO / DTO 中。当您修改了 POJO 中包含的数据时,您可能希望将这些修改存储回数据库。这里给出了一个例子

// A "mutable" POJO class
public class MyBook {
  public int id;
  public String title;
}

// Create a new POJO instance
MyBook myBook = new MyBook();
myBook.id = 10;
myBook.title = "Animal Farm";

// Load a jOOQ-generated BookRecord from your POJO
BookRecord book = create.newRecord(BOOK, myBook);

// Insert it (implicitly)
book.store();

// Insert it (explicitly)
create.executeInsert(book);

// or update it (ID = 10)
create.executeUpdate(book);

注意:由于您手动设置了 ID = 10,jOOQ 的 store() 方法将假定您要插入新记录。 有关此方面的更多详细信息,请参见手册中有关使用 UpdatableRecords 进行 CRUD的部分。

与 DAO 交互

如果您正在使用 jOOQ 的 代码生成器,则可以将其配置为为您生成 DAO。 这些 DAO 在生成的 POJO上运行。 这里给出了使用此类 DAO 的一个示例

// Initialise a Configuration
Configuration configuration = new DefaultConfiguration().set(connection).set(SQLDialect.ORACLE);

// Initialise the DAO with the Configuration
BookDao bookDao = new BookDao(configuration);

// Start using the DAO
Book book = bookDao.findById(5);

// Modify and update the POJO
book.setTitle("1984");
book.setPublishedIn(1948);
bookDao.update(book);

// Delete it again
bookDao.delete(book);
虽然这些 org.jooq.DAO 类型对于琐碎的操作看起来很有用,但随着您的 SQL 交互变得越来越复杂,它们很快就会变得不那么有趣了。就像 POJO 本身一样,DAO 非常简单。可以直接使用 org.jooq.UpdatableRecord 或 SQL 语句!

更复杂的数据结构

jOOQ 目前不支持更复杂的数据结构,Hibernate/JPA 尝试将关系数据映射到 POJO 的方式。 尽管不排除未来在这方面的发展,但 jOOQ 声称通用映射策略会导致巨大的额外复杂性,只能用于极少数的用例。 您很可能会找到一种使用 jOOQ 的各种提取模式的解决方案,并且客户端只有很少的样板代码。

反馈

您对此页面有任何反馈吗? 我们很乐意听取您的意见!

The jOOQ Logo