Hiberante_Annotation

Hiberante_Annotation<Tree>

Hibernate  Tree映射

 

提取树,都很熟悉了,数据结构中的经典的二叉树,学过数据结构的人都知道那四种遍历的方法,在Hibernate中所谓的树状映射,其实就是在一张表中保存的数据中,数据之间都有关系,好比一所大学,就说我的学校长安大学了,长安大学下面有公路学院汽车学院材料学院软件工程学院等等,那么每一个学院下面又有N多个系,依次类推,那么要是设计一张表,保存id和名字,将长安大学和她所有的学院和学院下所有的系保存起来,那么需要多添加一个字段,就是父Id比如说长安大学的id1,材料学院id2,那么材料学院的父id1,如此等等,每一项数据都有一个父id,这样就类似一棵树了,但不一定是二叉树,那么树的根节点是长安大学,其父idnull.

 

Hibernate中就可以用一个树状的ORM来编写实体了:

 

就拿上面的例子来说,有一个Node实体:

 

@Entity
@Table(name= "treeNode")
public class Node implements Serializable {
    private static finallong serialVersionUID = 2981633046554840135L;
    private int id;
    private Stringname;
    private Set<Node>children = new HashSet<Node>();
    private Nodeparent;
    @Id
    @GeneratedValue
    public int getId() {returnid; }
    publicvoid setId(int id) {this.id = id; }
    public String getName() {returnname; }
    publicvoid setName(String name) {this.name = name; }
    //一对多,就是自己与自己的孩子的关系
    @OneToMany(cascade=CascadeType.ALL,mappedBy = "parent",fetch=FetchType.EAGER)
    public Set<Node> getChildren() {returnchildren; }
    publicvoid setChildren(Set<Node> children) {this.children = children; }
    //多对一,就是自己和自己的父亲
    @ManyToOne
    @JoinColumn(name = "parent_id")//指定一个名字
    public Node getParent() {returnparent; }
    public void setParent(Node parent) {this.parent = parent; }
}


 

这里只用了一个实体类,.可以这么理解,我们在上面的长安大学的例子中列举了

学校à学院à院系,三层结构的数,就是树的深度为三,用原来的一对多关系映射,我们可以设计三张表:

第一张表保存学校信息;

第二张表保存学院信息,多一个应用学校主键的外键

第三张表保存院系信息,多一个应用学院主键的外键

 

这么设计也是可以完成的,不过这么设计的表的数目增多,而且回到了一对多的关系映射了,这里可以改进设计为一个树状的结构,所有信息就可以保存在一张表中,简化了结构,就是在表中设计一个父id,相当于原来的一方外键引用另一方主键,到这里是自己的的外键引用自己的主键,有些拗口,呵呵!

 

上面红色高亮显示的注解不难理解了,只不过这里是在一个实体里面,这跟之前的多对一的配置相似了。

@OneToMany(cascade=CascadeType.ALL,mappedBy= "parent",fetch=FetchType.EAGER)

fetch=FetchType.EAGER这个默认是lazy,懒加载的,在这里这么配置,是方便待会测试时的效果,因为如果不这么设置,待会去数据的时候,会取一条发一条,看不出树状结构。

 

按照上面的注解生成的建表语句:

    create table treeNode (
        id integer not null auto_increment,
        name varchar(255),
        parent_id integer,
        primary key (id)
    )
    alter table treeNode
        add index FK529C96C0D8DD21BF(parent_id),
        add constraint FK529C96C0D8DD21BF
        foreign key(parent_id)
       references treeNode (id)


自己的parent_id引用自己的主键id

 

再来一个保存数据的:

    

@Test
    public void testSave(){
       Node n1 = new Node();
       n1.setName("长安大学");
       Node n2 = new Node();
       n2.setName("软件工程学院");
       Node n3 = new Node();
       n3.setName("材料学院");
       Node n4 = new Node();
       n4.setName("软件设计");
       Node n5 = new Node();
       n5.setName("软件分析");
       Node n6 = new Node();
       n6.setName("材料成型机控制工程");
       Node n7 = new Node();
       n7.setName("金属材料");
       n1.getChildren().add(n2);
       n2.setParent(n1);
       n1.getChildren().add(n3);
       n3.setParent(n1);
       n2.getChildren().add(n4);
       n4.setParent(n2);
       n2.getChildren().add(n5);
       n5.setParent(n2);
       n3.getChildren().add(n6);
       n6.setParent(n3);
       n3.getChildren().add(n7);
       n7.setParent(n3);
       Session session = sf.openSession();
       Transaction tx = session.beginTransaction();
       session.save(n1);
       tx.commit();
    }

   

完了表中的数据是:

 Hiberante_Annotation<Tree&gt

来一个取出数据的测试,这里只用取出一个长安大学的根节点(树中的术语)

 

@Test
    publicvoid testLoad(){
       Session session = sf.openSession();
       Node root = (Node)session.load(Node.class, 1);
       diGuiPrint(root,0);
       session.close();
    }
    /**
     * 递归打印树状结构
     * @param root
     * @param level
     */
    privatevoid diGuiPrint(Node root,int level) {
       StringBuilder preStr = new StringBuilder("");
       for (int i = 0; i < level; i++) {
           preStr.append("---");
       }
       System.out.println(preStr+root.getName());
       for (Node child : root.getChildren()) {
           //如果是子节点,就在加三个"---"
           diGuiPrint(child,level+1);
       }
    }


 

打印的效果:

 

长安大学

---材料学院

------金属材料

------材料成型机控制工程

---软件工程学院

------软件设计

------软件分析

 

 

总结:上面加fetch的类型设置为EAGER了,如果表中的数据很大的时候,这种效率是很低的,如果数据不大,就可以用,然而不设置这个却得不到想要的效果,控制台输出时是一条sql一个结果,当然在hibernate.cfg.xml中去掉show_sql的属性,就可以了,这样就看不到sql了,当然这不是一个着眼于问题的方法,如果实在需要,我们可以用Ajax等远程的异步访问,用到的时候才取出数据。