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