分类 数据库 下的文章

关于SQL中left join的一些坑

最近写了一个十分复杂的SQL语句,涉及到了很多表的left join操作。最终的SQL语句执行速度惊人的慢,即使加了索引并且让大部分查询命中也会是一个难以忍受的等待时间。偶然间发现,本该为1000多条的结果居然实际是16万条。后来发现这种情况会出现在有一对多产生匹配时出现。
(暂且占个坑,以后补充)

Spring Data Rest中关联对象获取与批量对象获取

Spring Data Rest是非常快捷的实现Rest API的一个方案。但是,其中也有很多的坑需要处理。下面就记录下遇到的坑和处理方案:

有外键的对象如何添加/关联对象的POST数据格式是怎样的

假如有如下两个实体类,一个代表班级,一个代表学生,存在学生到班级的多对一单向映射:

@Entity
public class Clazz {
    private String id;
    // ......
}
@Entity
public class Student {
    
    private String id;
    private Clazz clazz;
    // ......
    
    @ManyToOne
    @JoinColumn(name = "clazzId")
    public Clazz getClazz() {
        return this.clazz;
    }

    // ......
}

使用Spring Data Rest添加student中class的正确格式是:

{
    // ......
    "clazz": "http://localhost:8080/api/clazzes/8a818fee5667b764015667ba27520002"
    // ......
}

也就是说,应该提交对应clazz的link。

如何选择性的展开外键对应的对象

Spring Data Rest的外键全部都是以link的方式给出,这样的方式在某些情况下很难以使用。比如,依然采用上一节的学生和班级实体,如果每个学生对应的班级都用link给出,那就意味着显示一个学生列表时每显示一条记录就要发一次请求以获取学生对应的班级。这样编程显然是一种灾难。那么,在返回的数据中直接嵌入clazz对象是合理的。Spring Data Rest提供了这样的机制来完成这种需求,那就是投影(projection)和摘录(excerpt)。
为了让学生列表直接携带班级信息,可以采用如下实现:

@Projection(name = "inlineClazz", types = { Clazz.class }) 
interface InlineClazz {

  // 这里写你要暴露的class字段的getter方法

  Clazz getClazz();    // 这句话会让clazz信息以内嵌(inline)的方式出现在学生的信息中

}

@RepositoryRestResource(excerptProjection = InlineClazz.class)
interface StudentRepository extends CrudRepository<Student, String> {}

一次性获取多个特定对象

例如,用一组ID获取一组题目的ID用以生成作业,可以用下面的方式实现:

@Query("from Problem p where (p.id in (:ids))")
List<Problem> fetchByIds(@Param("ids") List<String> ids);

用下面的方式获取数据

http://localhost:8080/rest/problems/search/fetchByIds?ids=62183,72111

这样就可以一次性取得ID为62183和72111的题目数据。

参考文献:

  1. 8.3. Excerpting commonly accessed data
  2. Selectively expand associations in Spring Data Rest response
  3. Spring Data REST: How to retrieve many items using list of Ids in one single call?

Hibernate自动生成创建时间和更新时间

Hibernate 4.3.8.final中,可以使用@CreateTimestamp以及@UpdateTimestamp注解自动生成时间戳。当然还需要配合@Generated注解使用。代码实例如下:

// 这是一个Hibernate实体(entity)
public class Example {

    // ......
    
    @CreationTimestamp
    @Temporal(TemporalType.TIMESTAMP)
    @Column(updatable = false)
    private Date createTime;

    @UpdateTimestamp
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModified;
    // ......
}