跳转至

JOOQ

初识JOOQ

JOOQ 是基于Java访问关系型数据库的工具包,轻量,简单,并且足够灵活,可以轻松的使用Java面向对象语法来实现各种复杂的sql。对于写Java的码农来说ORMS再也熟悉不过了,不管是Hibernate或者Mybatis,都能简单的使用实体映射来访问数据库。

get back in control of your sql

JOOQ的优点

  • DSL(Domain Specific Language )风格,代码够简单和清晰。遇到不会写的sql可以充分利用IDEA代码提示功能轻松完成。
  • 保留了传统ORM 的优点,简单操作性,安全性,类型安全等。不需要复杂的配置,并且可以利用Java 8 Stream API 做更加复杂的数据转换。

几个概念

jooq对表的抽象
jooq对字段的抽象
这里准确来说应该是Select族.Select有很多相似的类型,SelectConditionStep,SelectSelectStep等等,
是对查询一个封装。
jooq对查询条件的抽象,说白了就是where后面条件的一个集合
jooq对数据库数据的抽象,可以理解为数据库中的一条或者多条数据
jooq对查询结果集的封装,可以这样用:Record r = select xx,也可以这样玩:Result<Record>,目前基本上是用Result<Record>这种用法来存查询的结果集。
jooq的上下文,通过它,基本上可以做任何你想做的事,其它的元素,都可以通过这哥们得到,所以,搞懂了DSLContext,什么jooq都不是问题了。

准备

maven 里面引入

<dependency>
<groupId>com.zxy.common</groupId>
<artifactId>common-dao</artifactId>
</dependency>

使用

用spring注解进来

private CommonDao<Member> dao ;
@Autowired
public void setDao(CommonDao<Member> dao) {
    this.dao = dao;
}

get

 dao.get(String id);
 dao.getOptional(String id);

update

T update(T entity)
void update(Collection<T> entities)

// JOOQGenericDao源码  如果修改的实体有null值,但是数据库又是不能为null的,那么这个字段不会修改。
public T update(T entity) {
        this.record(entity, true, DSL.using(this.configuration)).update();
        return entity;
    }

 private UpdatableRecord<?> record(T object, boolean forUpdate, DSLContext context) {
        UpdatableRecord r = (UpdatableRecord)context.newRecord(this.table, object);
        if(forUpdate) {
            r.changed(this.primaryKey, false);
        }

        int size = r.size();

        for(int i = 0; i < size; ++i) {
            if(r.getValue(i) == null && !r.field(i).getDataType().nullable()) {
                r.changed(i, false);
            }
        }

        return r;
    }

delete

void delete(ID id)
void delete(Collection<ID> ids)
void delete(Condition... conditions)
void delete(Stream<Condition> conditions)
void deleteWithOptional(Stream<Optional<Condition>> conditions)

根据ID返还唯一对象,get查询不到会报错,getOptional返还可为空的对象.

fetch

  1. 只查询一列

    List<String> titles1 = create.select().from(BOOK).fetch().getValues(BOOK.TITLE);
    List<String> titles2 = create.select().from(BOOK).fetch(BOOK.TITLE);
    String[]     titles3 = create.select().from(BOOK).fetchArray(BOOK.TITLE);
    

  2. 查询一列并且类型转换

    List<Long> ids1 = create.select().from(BOOK).fetch().getValues(BOOK.ID, Long.class);
    List<Long> ids2 = create.select().from(BOOK).fetch(BOOK.ID, Long.class);
    Long[]     ids3 = create.select().from(BOOK).fetchArray(BOOK.ID, Long.class);
    

  3. 查询book的id,并且查询所有等于这个id的结果

    Map<Integer, BookRecord> map1 = create.selectFrom(BOOK).fetch().intoMap(BOOK.ID);
    Map<Integer, BookRecord> map2 = create.selectFrom(BOOK).fetchMap(BOOK.ID);
    Map<Integer, String>     map3 = create.selectFrom(BOOK).fetch().intoMap(BOOK.ID, BOOK.TITLE);
    Map<Integer, String>     map4 = create.selectFrom(BOOK).fetchMap(BOOK.ID, BOOK.TITLE);
    

  4. Group by AUTHOR_ID and list all books written by any author:

    Map<Integer, Result<BookRecord>> group1 = create.selectFrom(BOOK).fetch().intoGroups(BOOK.AUTHOR_ID);
    Map<Integer, Result<BookRecord>> group2 = create.selectFrom(BOOK).fetchGroups(BOOK.AUTHOR_ID);
    Map<Integer, List<String>>       group3 = create.selectFrom(BOOK).fetch().intoGroups(BOOK.AUTHOR_ID, BOOK.TITLE);
    Map<Integer, List<String>>       group4 = create.selectFrom(BOOK).fetchGroups(BOOK.AUTHOR_ID, BOOK.TITLE);
    

  5. fetchInto

    create.selectFrom(BOOK)
          .orderBy(BOOK.ID)
          .fetch()
          .into(new RecordHandler<BookRecord>() {
              @Override
              public void next(BookRecord book) {
                  Util.doThingsWithBook(book);
              }
          });
    // Or more concisely
    create.selectFrom(BOOK)
          .orderBy(BOOK.ID)
          .fetchInto(new RecordHandler<BookRecord>() {...});
    // Or even more concisely with Java 8's lambda expressions:
    create.selectFrom(BOOK)
          .orderBy(BOOK.ID)
          .fetchInto(book -> { Util.doThingsWithBook(book); }; );
    

项目实际参考

数据库分组查询

List<String> list = grantCommonDao.execute(x ->
                x.select(GRANT.OPERATOR_TYPES).from(GRANT)
                        .leftJoin(GRANT_DETAIL).on(GRANT.ID.eq(GRANT_DETAIL.GRANT_ID))
                        .leftJoin(ROLE).on(GRANT.ROLE_ID.eq(ROLE.ID))
                        .leftJoin(parentRole).on(GRANT.ROLE_ID.eq(parentRole.PARENT_ID))
                        .where(GRANT_DETAIL.MEMBER_ID.eq(memberId))
                        .and(GRANT_DETAIL.ORGANIZATION_ID.in(organizationIds))
                        .and(ROLE.ID.eq(roleId).or(parentRole.ID.eq(roleId)))
                        .groupBy(GRANT.OPERATOR_TYPES))
                .fetch().getValues(GRANT.OPERATOR_TYPES);

java8分组查询

Result<Record> record = courseChapterCommonDao.execute(
        x -> x.select(Fields.start().add(COURSE_CHAPTER).add(COURSE_CHAPTER_SECTION).end()).from(COURSE_CHAPTER)
                .leftJoin(COURSE_CHAPTER_SECTION).on(COURSE_CHAPTER.ID.eq(COURSE_CHAPTER_SECTION.CHAPTER_ID))
                .where(COURSE_CHAPTER.COURSE_ID.eq(courseId))
                .orderBy(COURSE_CHAPTER.SEQUENCE.desc(), COURSE_CHAPTER_SECTION.SEQUENCE.asc())
                .fetch());

Map<String, List<CourseChapterSection>> sectionMap = record.into(COURSE_CHAPTER_SECTION)
        .into(CourseChapterSection.class).stream().filter(x -> x.getId() != null)
        .collect(groupingBy(CourseChapterSection::getChapterId));

List<CourseChapter> courseChapters =  new ArrayList<>(record.into(COURSE_CHAPTER).into(CourseChapter.class).stream()
        .collect(toMap(CourseChapter::getId, p -> p, (p, q) -> q)).values());

courseChapters.forEach(x -> x.setCourseChapterSections(sectionMap.get(x.getId())));

courseChapters.sort((a, b) -> a.getSequence() - b.getSequence()); // 倒序

return courseChapters;

分页查询

com.zxy.product.course.jooq.tables.Organization organizationTable = ORGANIZATION.as("organization"); // 所属部门
com.zxy.product.course.jooq.tables.Member createUserTable = MEMBER.as("createUser"); // 创建人

SelectSelectStep<Record> selectListField = x
        .select(Fields.start().add(COURSE_INFO).add(organizationTable).add(createUserTable).end()); // 查询list
SelectSelectStep<Record> selectCountField = x.select(Fields.start().add(COURSE_INFO.ID.count()).end()); // 查询总条数

List<Condition> param = Stream
        .of(name.map(COURSE_INFO.NAME::contains), status.map(COURSE_INFO.STATUS::eq),
                createTime.map(COURSE_INFO.CREATE_TIME::gt),
                organizationId.map(COURSE_INFO.ORGANIZATION_ID::eq), type.map(COURSE_INFO.TYPE::eq))
        .filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());

Function<SelectSelectStep<Record>, SelectConditionStep<Record>> stepFunc = a -> {
    SelectConditionStep<Record> select = a.from(COURSE_INFO).leftJoin(organizationTable)
            .on(organizationTable.ID.eq(COURSE_INFO.ORGANIZATION_ID)).leftJoin(createUserTable)
            .on(createUserTable.ID.eq(COURSE_INFO.CREATE_MEMBER_ID)).where(param);

    itemScromType.map(stype -> select.andExists(x.select(COURSE_CHAPTER_SECTION.ID)
            .from(COURSE_CHAPTER_SECTION).where(COURSE_INFO.ID.eq(COURSE_CHAPTER_SECTION.COURSE_ID)
                    .and(COURSE_CHAPTER_SECTION.FILE_TYPE.eq(stype)))));
    return select;
};

int count = stepFunc.apply(selectCountField).fetchOne().getValue(0, Integer.class);

SelectConditionStep<Record> listSetp = stepFunc.apply(selectListField);
listSetp.orderBy(COURSE_INFO.CREATE_TIME.desc());
Result<Record> record = listSetp.limit((pageNum - 1) * pageSize, pageSize).fetch();

List<CourseInfo> courseInfoList = record.into(COURSE_INFO).into(CourseInfo.class);
List<Organization> organizationList = record.into(organizationTable).into(Organization.class);
List<Member> createUserList = record.into(createUserTable).into(Member.class);

IntStream.range(0, courseInfoList.size()).forEach(i -> {
    courseInfoList.get(i).setOrganization(organizationList.get(i));
    courseInfoList.get(i).setCreateUser(createUserList.get(i));
});
return PagedResult.create(count, courseInfoList);

参考