ログ日記

作業ログと日記とメモ

S2JDBCで再帰とかツリー構造とか

理想通り動きすぎて凄い!
s2jdbc-genを使って生成したEntityは特に変更する必要もなく、サービスに再帰SQLのコードを書くだけだった。

public class Category implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(precision = 10, nullable = false, unique = true)
    public Integer categoryId;

    @Column(precision = 10, nullable = false, unique = false)
    public String name;

    @Column(precision = 10, nullable = true, unique = false)
    public Integer parentId;

    @ManyToOne
    @JoinColumn(name = "parent_id", referencedColumnName = "category_id")
    public Category category;

    @OneToMany(mappedBy = "category")
    public List<Category> categoryList;
}

今回はルート(トップレベル?最上部?)に複数のデータがあったので、(最近のDBはnullでもインデックスが効くみたいだけど)便宜上のため category_id = 0, parent_id = null の ルートデータを登録しておく。
parent_idはDB上で外部キーを張っているのでルートは必ずnullになる。
しかしwhere句にnullは嫌なので、parent_id = 0 を実質的なルートとする。

public class CategoryService extends AbstractService<Category> {
    ...

    public List<Category> findAsTree(int categoryId){
        return select().innerJoin(categoryList())
                       .leftOuterJoin(categoryList().categoryList()) // ※
                       .where(new SimpleWhere().eq(parentId(), 0)
                       .getResultList();
    }

}

これでツリー構造のDBのデータを取得できる。特に悩むこともなかった。
あとはDBの階層に合わせて ※ の部分を増やせばいくらでも深く掘れる。leafまでの階層が固定ではないならleft outer joinを使う。最低三階層あるよって場合はinnerJoinで三つ繋げてあとはouterJoin。


エンティティデータはcategoryListをたどっていって取得する。
受け取るエンティティが既にツリー構造になっている。素晴らしい。


そもそもRDBMSでツリー構造のテーブルを作るのが良いのかどうか分からんけども、膨大な量でなければ十分使えそうだ。