【每日鲜蘑】Quarkus使用了Panache来简化数据库操作🔥

7,107 阅读2分钟

使用Hibernate ORM的过程中,会有很多烦人的事情发生。

  • ID逻辑,通常我们并不关心ID是怎么生成的,只是用做主键。
  • 大量的gettersetter方法,受益于贫血模型,但在使用中,我们只是使用这些方法来获取或者设置属性。
  • 早起的Java EE模式建议我们将EntityDAO的进行划分,但在使用中,其实DAO通常都是空置的,这种多余的层级结构没有带来应有的价值。
  • Hibernate提供了强大的通用的方法,但是大多数并没有被用到,这是一种浪费。
  • 实际使用中,Hibernate在大多数的琐碎查询中显得更加复杂。

引入

<dependencies>
    <!-- Hibernate ORM specific dependencies -->
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-hibernate-orm-panache</artifactId>
    </dependency>

    <!-- JDBC driver dependencies -->
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-jdbc-mysql</artifactId>
    </dependency>
</dependencies>

配置文件

quarkus.datasource.db-kind=mysql
quarkus.datasource.username=root
quarkus.datasource.password=123456
quarkus.datasource.jdbc.url=jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=utf8&useSSL=false
quarkus.datasource.jdbc.max-size=8
quarkus.datasource.jdbc.min-size=1

# 日志
quarkus.hibernate-orm.log.sql=true
# 初始化脚本
quarkus.hibernate-orm.sql-load-script=import.sql
# 自动创建表
quarkus.hibernate-orm.database.generation=drop-and-create

定义数据库实体

通过继承PanacheEntity 或者 PanacheEntityBase,下面简单介绍了下基础的编码操作,Panache可以支持链式调用的,也可以返回Stream

默认ID

@Entity
public class Person extends PanacheEntity {
    public String name;
    public LocalDate birth;
    public Status status;
}

自定义ID

@Entity
public class Person extends PanacheEntityBase {
    @Id
    @SequenceGenerator(
            name = "personSequence",
            sequenceName = "person_id_seq",
            allocationSize = 1,
            initialValue = 4)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "personSequence")
    public Integer id;
    public String name;
    public LocalDate birth;
    public Status status;
}

定义的字段是public的,其实底层还是通过gettersetter来进行访问的,所以我们可以通过编写gettersetter来进行特殊处理。

基本操作

新增

@Transactional
public Person create() {
    Person person = new Person();
    person.name = "Stef";
    person.birth = LocalDate.of(1910, Month.FEBRUARY, 1);
    person.status = Status.Alive;
    // 进行持久化
    person.persist();
    return person;
}

单条查询

查询实体

Person person = Person.findById(personId);

查询实体Optional

Optional<Person> optional = Person.findByIdOptional(personId);
Person person = optional.orElseThrow(() -> new NotFoundException());

查询列表

查询所有

List<Person> allPersons = Person.listAll();
long countAll = Person.count();

条件查询

List<Person> livingPersons = Person.list("status", Status.Alive);
long countAlive = Person.count("status", Status.Alive);

分页查询

PanacheQuery<Person> livingPersons = Person.find("status", Status.Alive);
// 每页 10 条,返回第 5 页
List<Person> page5 = livingPersons.page(Page.of(5, 10)).list();
// 总条数
long count = livingPersons.count();

排序

// 方式1
List<Person> persons = Person.list("order by name,birth");

// 方式2
List<Person> persons = Person.list(Sort.by("name").and("birth"));

// 方式3
List<Person> persons = Person.list("status", Sort.by("name").and("birth"), Status.Alive);

更新

单条更新

Optional<Person> optional = Person.findByIdOptional(personId);
Person person = optional.orElseThrow(() -> new NotFoundException());
person.name = "更改name";

批量更新

Person.update("name = 'Moral' where status = ?1", Status.Alive);

删除

单条删除

Optional<Person> optional = Person.findByIdOptional(personId);
Person person = optional.orElseThrow(() -> new NotFoundException());
person.delete();

删除全部

Person.deleteAll();

按条件删除

Person.delete("status", Status.Alive);

HQL的使用

native 语句

Order.find("select distinct o from Order o left join fetch o.lineItems");
Order.update("update from Person set name = 'Moral' where status = ?", Status.Alive);

查询语句

Person.find("name = ?1 and status = ?2", "stef", Status.Alive);

使用Map作为参数

Map<String, Object> params = new HashMap<>();
params.put("name", "stef");
params.put("status", Status.Alive);
Person.find("name = :name and status = :status", params);

使用Parameters定义参数

// 使用map
Person.find("name = :name and status = :status",
         Parameters.with("name", "stef").and("status", Status.Alive).map());

// 不用map
Person.find("name = :name and status = :status",
         Parameters.with("name", "stef").and("status", Status.Alive));

使用数据库锁

// 方式1
Person p = Person.findById(id, LockModeType.PESSIMISTIC_WRITE);

// 方式2
Person p = Person.find("name", name).withLock(LockModeType.PESSIMISTIC_WRITE).findOne();

另外的使用方式之Repository

@Entity
public class Person {
    @Id @GeneratedValue private Long id;
    private String name;
    private LocalDate birth;
    private Status status;

    // getter and setter ...
}

@ApplicationScoped
public class PersonRepository implements PanacheRepository<Person> {
}

这种方式与 Spring Data JPA 比较类似了,就不做介绍了。

@GET
@Operation(summary = "查询记录列表")
public PageResponse<SysUserModel> list(
    @QueryParam("key") String key,
    @QueryParam("index") int index,
    @QueryParam("size") int size
) {
    StringBuilder sql = new StringBuilder("1 = 1 ");
    Parameters parameters = new Parameters();
    if (Objects.nonNull(key)) {
        sql.append("and userName like :userName");
        parameters.and("userName", "%" + key + "%");
    }
    PanacheQuery<SysUserModel> query = SysUserModel.find(sql.toString(), parameters);
    query.page(Page.of(index, size == 0 ? 10 : size));
    List<SysUserModel> list = query.list();
    list.forEach(sysUserModel -> sysUserModel.password=null);
    long count = query.count();
    return PageResponse.page(count, list);
}