hibernate注解版关联映射Many-to-One/Many-to-Many等&异常处理

31 篇文章 0 订阅
23 篇文章 0 订阅

应用实例:

一对多和多对一:
warning.class 控制
@JsonIgnore
@ManyToOne(cascade={CascadeType.MERGE})@JoinColumn(name = "from_id")private User sender;
User.class 被控
@JsonIgnore
@OneToMany(mappedBy = "sender", fetch = FetchType.LAZY, cascade = {CascadeType.MERGE})
@OrderBy(value = "id DESC")
private Set<Warning> handleWarnings = new HashSet<Warning>();


一对多联级保存:

参考:http://bbs.csdn.net/topics/380236657 或 http://blog.csdn.net/lztyll123/article/details/7389181/
多方:
@OneToMany(mappedBy = "notice", fetch=FetchType.EAGER, cascade = {CascadeType.ALL})
private Set<NoticeGage> gages = new HashSet<NoticeGage>();

一方:
@JsonIgnore
@ManyToOne
@JoinColumn(name = "notice_id")
private Notice notice;

测试代码(注意—— 多方新建的时候需要设置一方,否则报错多方外键为空)
Notice notice = new Notice(NoticeTypeEnum.PLEDGE_START, contractService.getAll().get(0),userService.getById(9).getData());
notice.getGages().add(new NoticeGage(notice, gageService.getById(1),50));
noticeService.save(notice);




多对多:
User.class  被控方
@JsonIgnore
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "receivers")
private Set<Warning> receiveWarnings = new HashSet<Warning>();

Warning.class  控制方(需要额外的warning_user的多对多关系表,双id主键外键)
@ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
@JoinTable(name = "warning_user",
        joinColumns = {@JoinColumn(name = "warning_id")},
        inverseJoinColumns = {@JoinColumn(name = "user_id")})
private Set<User> receivers = new HashSet<User>();



在你不需要的转化json的属性上面设置@JsonIgnore

异常处理:

摘自:hibernate几个 OneToMany.错误总结

错误一

org.hibernate.AnnotationException:  Collection has neither generic type or OneToMany.targetEntity() defined: com.hibernate.SubStation. bays

解决方法
org.hibernate.AnnotationException:  Collection has neither generic type or OneToMany.targetEntity() defined: org.svse.org.model.Order. orderItems

在用annotation做数据库映射一对多和多对一的时候要注意:因为这个问题只有在用annotation的时候才有,用xml文件的时候就没有在一对多关系映射中一的一方直接写Set就可以不用指定相应的元素类型!!

请检查orderItems这个属性是否使用了泛型,如:Set<OrderItem>. 
如果未使用泛型,请在OneToMany注释中使用targetEntity指定元素的类型


错误二

org.hibernate.AnnotationException:  mappedBy reference an unknown target entity property: com.hibernate.Bay.group in com.hibernate.SubStation.bays


解决方法
在用annotation做数据库映射一对多和多对一的时候要注意:
@OneToMany(mappedBy = "news_type", cascade = CascadeType.ALL,fetch = FetchType.LAZY)  这里的mappedBy 要对应
@ManyToOne
@JoinColumn(name="news_type", nullable=true, insertable = true, updatable = true) 
private NewsType newsType;的NewsType对象名,也就是newsType而不是字段名news_type。


错误三

node to traverse cannot be null!


出现这种问题是因为HQL语句出现问题,引起内部查询对象为空,无法处理为空值引起的。解决方法,检查HQL语句(尤其是关键字)。



================================================================================================

转自:http://blog.csdn.net/maoyeqiu/article/details/50397078

 

使用注解的Hibernate one-to-one映射

几种支持的技术

在Hibernate中,在两个实体间有三种方式创建one-to-one的关系,其中一种方式是使用@OneToOne注解。第一种技术广泛的被使用,在表中使用外键列;第二种技术使用众所周知的第三个表,来存储两个表间的映射关系;第三种技术是在两个表中使用普通的主键。
我们一个一个来看看

使用主键的关系

在这种关系中,外键列在自己的实体中创建,例如我们让 Employee实体作为拥有者,那么外键列ACCOUNT_ID将会在Employee表中被创建,这一列将为Account表存储外键
表的结构如下:

为了建立这种联系,在Employee实体类中如下指定account实体
[java]  view plain  copy
  1. @OneToOne  
  2. @JoinColumn(name="ACCOUNT_ID")  
  3. private AccountEntity account;  

join列使用 @JoinColumn注解声明,这很像@Column注解。它有很多参数指定列名,这个参数声明了将要join的目标实体的列。如果没有@JoinColumn在拥有端进行声明的话,它是默认增加的。它的名字将会连接拥有端_(下划线)和被拥有端的主键列。在双向的关系中,一边(仅仅一边)必须要作为拥有者,拥有者负责关联列的更新。为了声明另一边不负责这个关系,可以使用mappedBy。mappedBy指的是关联的拥有者端的属性的名字
[java]  view plain  copy
  1. @OneToOne(mappedBy="account")  
  2. private EmployeeEntity employee;  

上边的属性声明了它依赖于拥有实体的映射关系
我们来测试一下上边的映射关系
[java]  view plain  copy
  1. public class TestForeignKeyAssociation {  
  2.    
  3.     public static void main(String[] args) {  
  4.         Session session = HibernateUtil.getSessionFactory().openSession();  
  5.         session.beginTransaction();  
  6.    
  7.         AccountEntity account = new AccountEntity();  
  8.         account.setAccountNumber("123-345-65454");  
  9.    
  10.         // Add new Employee object  
  11.         EmployeeEntity emp = new EmployeeEntity();  
  12.         emp.setEmail("demo-user@mail.com");  
  13.         emp.setFirstName("demo");  
  14.         emp.setLastName("user");  
  15.    
  16.         // Save Account  
  17.         session.saveOrUpdate(account);  
  18.         // Save Employee  
  19.         emp.setAccount(account);  
  20.         session.saveOrUpdate(emp);  
  21.    
  22.         session.getTransaction().commit();  
  23.         HibernateUtil.shutdown();  
  24.     }  
  25. }  

运行结果:
[java]  view plain  copy
  1. Hibernate: insert into ACCOUNT (ACC_NUMBER) values (?)  
  2. Hibernate: insert into Employee (ACCOUNT_ID, EMAIL, FIRST_NAME, LAST_NAME) values (?, ?, ?, ?)  

使用普通的连接表

这种方法对我们来说并不是新的方法,我们从它的关系表开始

在这种技术中,主要使用的标签是@JoinTable,这种标签定义新表的名字(强制性的)和两个表的外键,我们来看一下他们是怎么使用的
[java]  view plain  copy
  1. @OneToOne(cascade = CascadeType.ALL)  
  2. @JoinTable(name="EMPLOYEE_ACCCOUNT", joinColumns = @JoinColumn(name="EMPLOYEE_ID"),  
  3. inverseJoinColumns = @JoinColumn(name="ACCOUNT_ID"))  
  4. private AccountEntity account;  

@JoinTable注解用在Employee实体类中,被声明为一个新的表EMPLOYEE_ACCOUNT将会创建两个列EMPLOYEE_ID(EMPLOYEE表的主键)和ACCOUNT_ID(ACCOUNT表的主键),测试上述的实体将会产生下边的SQL结果
[java]  view plain  copy
  1. Hibernate: insert into ACCOUNT (ACC_NUMBER) values (?)  
  2. Hibernate: insert into Employee (EMAIL, FIRST_NAME, LAST_NAME) values (?, ?, ?)  
  3. Hibernate: insert into EMPLOYEE_ACCCOUNT (ACCOUNT_ID, EMPLOYEE_ID) values (?, ?)  

使用共享主键

在这种技术中,Hibernate将会确保在两个表中使用了普通的主键。这种方式Employee的实体主键可以被安全的假定为Account实体的主键
表间的结构图如下:

这种方式,主要使用的是@PrimaryKeyJoinColumn注解
[java]  view plain  copy
  1. @OneToOne(cascade = CascadeType.ALL)  
  2. @PrimaryKeyJoinColumn  
  3. private AccountEntity account;  

在Account实体的那一边,依旧根据拥有者边的映射关系
[java]  view plain  copy
  1. @OneToOne(mappedBy="account", cascade=CascadeType.ALL)  
  2. private EmployeeEntity employee;  

测试上边实体的映射关系
[java]  view plain  copy
  1. Hibernate: insert into ACCOUNT (ACC_NUMBER) values (?)  
  2. Hibernate: insert into Employee (ACCOUNT_ID, EMAIL, FIRST_NAME, LAST_NAME) values (?, ?, ?, ?)  

我们在Hibernate中有三种类型的one to one 映射关系
可以在原文中下载源码:http://howtodoinjava.com/2012/11/15/hibernate-one-to-one-mapping-using-annotations/


-------------------------------------------------------------------------------------------------------------------------------------------------------------------

注解版关系映射Many-to-Many,Many-to-One:

摘自:

hibernate注解版关联映射 详解

属性介绍:

1.多对一:

  1. @ManyToOne(fetch=FetchTypecascade=CascadeType)


@ManyToOne表示一个多对一的映射,该注解标注的属性通常是数据库表的外键 
optional:是否允许该字段为null,该属性应该根据数据库表的外键约束来确定,默认为true 
fetch:表示抓取策略,默认为FetchType.EAGER 
cascade:表示默认的级联操作策略,可以指定为ALL,PERSIST,MERGE,REFRESH和REMOVE中的若干组合,默认为无级联操作 
targetEntity:表示该属性关联的实体类型.该属性通常不必指定,ORM框架根据属性类型自动判断targetEntity. 
示例: 
@ManyToOne表示一个多对一的映射,该注解标注的属性通常是数据库表的外键 
optional:是否允许该字段为null,该属性应该根据数据库表的外键约束来确定,默认为true 
fetch:表示抓取策略,默认为FetchType.EAGER 
cascade:表示默认的级联操作策略,可以指定为ALL,PERSIST,MERGE,REFRESH和REMOVE中的若干组合,默认为无级联操作 
targetEntity:表示该属性关联的实体类型.该属性通常不必指定,ORM框架根据属性类型自动判断targetEntity. 
示例: 
@ManyToOne表示一个多对一的映射,该注解标注的属性通常是数据库表的外键 
optional:是否允许该字段为null,该属性应该根据数据库表的外键约束来确定,默认为true 
fetch:表示抓取策略,默认为FetchType.EAGER 
cascade:表示默认的级联操作策略,可以指定为ALL,PERSIST,MERGE,REFRESH和REMOVE中的若干组合,默认为无级联操作 
targetEntity:表示该属性关联的实体类型.该属性通常不必指定,ORM框架根据属性类型自动判断targetEntity. 
示例:

  1. //订单Order和用户User是一个ManyToOne的关系   
  2.     //在Order类中定义   
  3.     @ManyToOne()   
  4.     @JoinColumn(name="USER")   
  5.     public User getUser() {   
  6.        return user;   
  7.     }    


2.JoinColumn

  1. @JoinColumn


@JoinColumn和@Column类似,介量描述的不是一个简单字段,而一一个关联字段,例如.描述一个@ManyToOne的字段. 
name:该字段的名称.由于@JoinColumn描述的是一个关联字段,如ManyToOne,则默认的名称由其关联的实体决定. 
例如,实体Order有一个user属性来关联实体User,则Order的user属性为一个外键, 
其默认的名称为实体User的名称+下划线+实体User的主键名称 
示例:见@ManyToOne

3.一对多:

  1. @OneToMany(fetch=FetchType,cascade=CascadeType)   


@OneToMany描述一个一对多的关联,该属性应该为集体类型,在数据库中并没有实际字段. 
fetch:表示抓取策略,默认为FetchType.LAZY,因为关联的多个对象通常不必从数据库预先读取到内存 
cascade:表示级联操作策略,对于OneToMany类型的关联非常重要,通常该实体更新或删除时,其关联的实体也应当被更新删除 
例如:实体User和Order是OneToMany的关系,则实体User被删除时,其关联的实体Order也应该被全部删除 
示例: 
@OneToMany描述一个一对多的关联,该属性应该为集体类型,在数据库中并没有实际字段. 
fetch:表示抓取策略,默认为FetchType.LAZY,因为关联的多个对象通常不必从数据库预先读取到内存 
cascade:表示级联操作策略,对于OneToMany类型的关联非常重要,通常该实体更新或删除时,其关联的实体也应当被更新删除 
例如:实体User和Order是OneToMany的关系,则实体User被删除时,其关联的实体Order也应该被全部删除 
示例: 

  1. @OneTyMany(cascade=ALL)   
  2.     public List getOrders() {   
  3.        return orders;   
  4.     }   


4.One-to-One

  1. @OneToOne(fetch=FetchType,cascade=CascadeType)


@OneToOne描述一个一对一的关联 
fetch:表示抓取策略,默认为FetchType.LAZY 
cascade:表示级联操作策略 
示例: 
@OneToOne描述一个一对一的关联 
fetch:表示抓取策略,默认为FetchType.LAZY 
cascade:表示级联操作策略 
示例: 

  1. @OneToOne(fetch=FetchType.LAZY)   
  2.     public Blog getBlog() {   
  3.        return blog;   
  4.     }   


5.Many-to-Many

  1. @ManyToMany   


@ManyToMany 描述一个多对多的关联.多对多关联上是两个一对多关联,但是在ManyToMany描述中,中间表是由ORM框架自动处理 
targetEntity:表示多对多关联的另一个实体类的全名,例如:package.Book.class 
mappedBy:表示多对多关联的另一个实体类的对应集合属性名称 
示例: 
User实体表示用户,Book实体表示书籍,为了描述用户收藏的书籍,可以在User和Book之间建立ManyToMany关联@ManyToMany 描述一个多对多的关联.多对多关联上是两个一对多关联,但是在ManyToMany描述中,中间表是由ORM框架自动处理
targetEntity:表示多对多关联的另一个实体类的全名,例如:package.Book.class 
mappedBy:表示多对多关联的另一个实体类的对应集合属性名称 
示例: 
User实体表示用户,Book实体表示书籍,为了描述用户收藏的书籍,可以在User和Book之间建立ManyToMany关联

  1. @Entity   
  2.     public class User {   
  3.        private List books;   
  4.        @ManyToMany(targetEntity=package.Book.class)   
  5.        public List getBooks() {   
  6.            return books;   
  7.        }   
  8.        public void setBooks(List books) {   
  9.            this.books=books;   
  10.        }   
  11.     }     
  12.   
  13.     @Entity   
  14.     public class Book {   
  15.        private List users;   
  16.        @ManyToMany(targetEntity=package.Users.class, mappedBy="books")   
  17.        public List getUsers() {   
  18.            return users;   
  19.        }   
  20.   
  21.        public void setUsers(List users) {   
  22.            this.users=users;   
  23.        }   
  24.     }   


两个实体间相互关联的属性必须标记为@ManyToMany,并相互指定targetEntity属性, 
需要注意的是,有且只有一个实体的@ManyToMany注解需要指定mappedBy属性,指向targetEntity的集合属性名称 
利用ORM工具自动生成的表除了User和Book表外,还自动生成了一个User_Book表,用于实现多对多关联 

再接再厉,解决第三个问题:本来想排一下序,怎么也这么难啊。难也得搞定是吧^_^,前面以为orderBy按字段来呢,结果一个文档,原来是被关联实体中一个属性,哎,细心还是好啊。 再补充一点:orderby的方式对list是有效的,set和map用mapkey 。

两个实体间相互关联的属性必须标记为@ManyToMany,并相互指定targetEntity属性,
需要注意的是,有且只有一个实体的@ManyToMany注解需要指定mappedBy属性,指向targetEntity的集合属性名称 
利用ORM工具自动生成的表除了User和Book表外,还自动生成了一个User_Book表,用于实现多对多关联

。。。。。。(详见原文:http://blog.csdn.net/superdog007/article/details/8534443)


摘自: 

深入理解Hibernate表与表之间的关联,搞懂cascade、inverse、lazy等属性,和注解版Hibernate实例


1.搞定Hibernate表与表之间的关联,搞懂cascade、inverse、lazy等属性

2个类,书(Book),类别(BookType)

public class Book {
    private Long id;
    private BookType type;
}


public class BookType {
    private Long id;
}

好了,我们开始

情况一

它们相互不关联

情况二

   /**
     * @hibernate.many-to-one column="col_booktype_id"
     */    
    public BookType getType() {
        return type;
    }

也就是说,一个类有多本书,比如有很多书是历史类别,很多其他书是数学类别,非常好理解

那么:

        BookType bookType = new BookType();        
        
        Book book1 = new Book();
        book1.setType(bookType);
        
        Book book2 = new Book();
        book2.setType(bookType);

        bookService.save(book1);
        bookService.save(book2);

先创建一个 “书本类” 的实例,再创建 2本书,都是属于这本书,保存这2本书,结果出错。为什么?因为没有把BookTpye保存,所以那2本书的类别字段就不能保存了。这时cascade登场了——
cascade有2个级别   cascade=“save-update”   cascade=“delete”   我们先来看 cascade=“save-update”


   /**
     * @hibernate.many-to-one column="col_booktype_id" cascade=“save-update”
     */    
    public BookType getType() {
        return type;
    }

这时,在保存book1的时候自动能把booktype也保存掉,在保存book2的时候,发现booktype已经存在了,于是去更新了booktype,总之要级联过去。

所以说,其实这个功能是比较偷懒的功能,呵呵。

好了,现在我们来看看  cascade=“delete” 

假设上述已经都保存完毕,那么把book1删除,OK……接着删除book2,这时,它会先去删除booktype,再删除book2,可见就是这个意思。Hibernate会认为,你历史类的书都没了,我还要历史这个类干嘛用?于是都删了。

情况三

那么现在我们有一个新的需求了,要通过booktype去得到book,比如 getBooks(“历史”) 返回一个book的Set,在没有Hibernate这些框架的时代,我们要先去 booktype表,找历史的那个id,再拿这个Id去book中,把这些book给找出来,当然,有了Hibernate,其实内部也是这么执行的,只 是大大简化了我们的编码量。好!那么这个当然是属于 one-to-many了,我们在BookType中这么去设置

        <set name="books" lazy="false" inverse="true" cascade="all">
            <key column="col_booktype_id"></key>
            <one-to-many class="hdu.management.library528.entity.Book"/>
        </set>

我们一个个属性来讲解,这里的set其实没有对应数据库的任何表和任何字段,首先这点大家要明确。
lazy     当设置为true的时候——我们得到一个 历史类 ,并没有把这个类里的书全部读出来,而是当用的时候才去读
当设置为false的时候,我们得到一个历史类,里面已经把books都封装好了,全部读出来了
很好理解吧?lazy就是懒惰的意思嘛。这里有点需要注意,一般我们在用spring进行事务操作的时候,当lazy=“true”的时候很容易会出错。解决办法:
一、如果不考虑效率的话,大家可以干脆把 lazy=“false”
二、可以去参考下 openSessionInView这个spring的拦截器

好了,我们讲重点吧,首先来看cascade="all"  这个其实不用理解的很难,和many-to-one是一样的,我们这么理解,cascade是写在哪个类里的?答:booktype。它在对哪个类进行声 明?答:这里是对book进行声明。 好了,那么意思就是,我们在删除booktype和更新添加的时候,需不需要对book也进行操作的意思。
比如,在情况二中,我们是无法对booktype进行删除的,因为有外键关联它,那么在这里我们设置了one-to-many后,OK了,当我们删除booktype的时候,会先把相关的book全部删除,接着把booktype给删除。

最后讲讲重点   inverse="true"  我在刚刚接触Hibernate的时候,对inverse、cascade这两个东西最为头痛,现在cascade已经理解的非常清楚了吧?那么我来总结inverse
第一、概念,指定要不要当自己来维护关系。(其实根本不用去理解)
第二、在one-to-many的时候,设置inverse=“true” 当然是在 one这个地方设置
第三、在many-to-many的时候,随便在哪端先设置inverse=“true”,另一个地方设置inverse=“false”
第四、别问我:“那我不这么设置行不行,换个方法设置下看看”,取消念头,回到第二和第三条。

情况四

现在又有了新的要求,刚才一直是 一个书本类下面有很多书,那么在现实中,一本书它也有可能是属于很多类别的,比如它既是历史类的又是属于地理类的。这个时候就要用many-to- many了。前面的关系中,我们用的是2张表来维护相互之间的关系,当多对多的时候,就需要在构建出一张表来维护了。
这里我们构建出一个  书本——书本类   这样一张表 ,里面有2个字段,书本ID,书本类ID,在映射到Oracle中是自动转换成联合主键的,没有重复。

    /** *//**
     * @hibernate.set table="lib_book_booktype" lazy="false" cascade="all" inverse="true"
     *               
     * @hibernate.collection-key column="col_book_id"
     * @hibernate.collection-many-to-many column="col_booktype_id" class="hdu.management.library528.entity.BookType"
     */

    public Set getBookTypes() ...{
        return bookTypes;
    }


    /** *//**
     * @hibernate.set table="lib_book_booktype" lazy="false" cascade="all"
     *               
     * @hibernate.collection-key column="col_booktype_id"
     * @hibernate.collection-many-to-many column="col_book_id" class="hdu.management.library528.entity.Book"
     */
    public Set getBooks() ...{
        return books;
    }
注意:不要忘记  private Set books = new HashSet();

设置和上面一样,意思也一样,唯一不同的就是多了个 table,column自然也要指向那个table了,这是设置之后,其实book和booktype这2张表图了个安宁,什么外键都没有了,只是他们的关系表在控制它们两个了。


我们继续针对,在多对多中,inverse的问题,这里我们把 book  的inverse=true    booktype的inverse=false   当我们做这样的操作时


        book1.getBookTypes().add(bookType1);
        book1.getBookTypes().add(bookType2);
        book1.getBookTypes().add(bookType3);

        bookService.saveOrUpdate(book1);


会发现 book1  保存了,bookType1、2、3也保存了,但是他们的关系却没有保存,为什么?因为 book 把关系的inverse=true了,踢给别人去处理了,所以它不来理会关系的处理。所以,我们就要对 booktype这么操作才可以处理关系。当然如果,2端我们都去设置 inverse=false的话,都可以操作了,至于说效率方面的考虑,呵呵……先就不用管了。



2.hibernate之级联cascade和关系维持inverse


hibernate的关联关系,重点在理解级联cascade和inverse

 

1、cascade一般用在级联保存,级联更新,级联删除上

 

   1.1cascade注解有两种,一种是基于hibernate注解

org.hibernate.annotations.Cascade

org.hibernate.annotations.CascadeType

支持一下级联

ALL, PERSIST//级联持久化,调用session.persist()时会触发级联事件 MERGE//级联保存或者更新,jpa规范 hibernate为了支持jsr220 后面添加的,调用session.merge()时触发级联 REMOVE,//级联删除,jpa规范同上,调用session.delete()时触发 REFRESH, DELETE,//级联删除,session.delete()触发 SAVE_UPDATE,//级联保存或者更新session.save(),session.update(),session.saveOrUpdate(); REPLICATE,

    配置示例

 

Java代码  复制代码  收藏代码
  1. @Cascade(value={org.hibernate.annotations.CascadeType.ALL})   
  2.     private StudentInfo studentInfo ;  
[java]  view plain copy
  1. @Cascade(value={org.hibernate.annotations.CascadeType.ALL})  
  2.     private StudentInfo studentInfo ;  
 

 

   1.2第二种注解是基于jpa规范,也就是apache jsr220规范,也是ejb3的持久层规范

javax.persistence.CascadeType

ALL, 

        PERSIST,调用session.persist()时触发 MERGE,调用session.merge()触发    REMOVE,调用session.delete()触发    REFRESH,      DETACH

    配置示例

 

Java代码  复制代码  收藏代码
  1. @ManyToOne(cascade={CascadeType.MERGE})   
  2.     @JoinColumn(name = "teacher_id")   
  3.     private Teacher teacher;  
[java]  view plain copy
  1. @ManyToOne(cascade={CascadeType.MERGE})  
  2.     @JoinColumn(name = "teacher_id")  
  3.     private Teacher teacher;  

 

  1.3级联一般用在OneToOne和OneToMany上,这也是hibernate官方的推荐,有时候我们在开发中也用在            ManyToOne,ManyToMany上,只是想在测试上少写点代码而已,在持久化一个实体的时候级联持久其他关联实体,

  如下:teacher和student是ManyToMany,加上了cascade注解,便于测试

 

Java代码  复制代码  收藏代码
  1. @Test  
  2.     public void addStudentToTeacher(){   
  3.         Student student1 = new Student("张三",20,20072733L);   
  4.         Student student2 = new Student("李四",20,20072734L);   
  5.         Student student3 = new Student("王五",20,20072735L);   
  6.            
  7.         Teacher teacher = new Teacher("张老师");   
  8.         teacher.getStudents().add(student3);   
  9.         teacher.getStudents().add(student2);   
  10.         teacher.getStudents().add(student1);   
  11.         this.teacherDao.save(teacher);   
  12.     }  
[java]  view plain copy
  1. @Test  
  2.     public void addStudentToTeacher(){  
  3.         Student student1 = new Student("张三",20,20072733L);  
  4.         Student student2 = new Student("李四",20,20072734L);  
  5.         Student student3 = new Student("王五",20,20072735L);  
  6.           
  7.         Teacher teacher = new Teacher("张老师");  
  8.         teacher.getStudents().add(student3);  
  9.         teacher.getStudents().add(student2);  
  10.         teacher.getStudents().add(student1);  
  11.         this.teacherDao.save(teacher);  
  12.     }  

 

2、inverse,英文意思是反向,反转。在这里可以理解为控制反转,也就是说实体间的关系由谁控制,所以inverse用在实体关联上。如OneToOne,OneToMany,ManyToMany 

     在OneToMany中,如果不指定inverse,那么hibernate会去找默认的表来维持关系。

     例如用老师和课程两个实体来说明,假设teacher和course是OneToMany的关系,

     配置如下:

     //通过外键teacher_id关联teacher,inverse通过mappedBy来设置

Java代码  复制代码  收藏代码
  1. @ManyToOne(cascade={CascadeType.MERGE})   
  2.     @JoinColumn(name = "teacher_id")   
  3.     private Teacher teacher;   
  4.   
  5. @OneToMany(mappedBy="teacher",fetch=FetchType.LAZY,cascade={CascadeType.MERGE,CascadeType.REMOVE})   
  6.     private Set<Course> courses = new HashSet<Course>();  
[java]  view plain copy
  1. @ManyToOne(cascade={CascadeType.MERGE})  
  2.     @JoinColumn(name = "teacher_id")  
  3.     private Teacher teacher;  
  4.   
  5. @OneToMany(mappedBy="teacher",fetch=FetchType.LAZY,cascade={CascadeType.MERGE,CascadeType.REMOVE})  
  6.     private Set<Course> courses = new HashSet<Course>();  

   在这里指定teacher的属性courses的关系由关联实体Course的teacher属性维持,如果不配置,则会去找关联表teacher_course来维持关系。OneToOne和ManyToMany大同小异,这里不再累述!


4.Hibernate注解版,多对一,一对多关系映射


双向一对多关系,一是关系维护端(owner side),多是关系被维护端(inverse side)。在关系被维护端需要通过@JoinColumn建立外键列指向关系维护端的主键列

 
 
publicclass  Order  implements  Serializable {
     private Set<OrderItem> orderItems  =  new  HashSet<OrderItem>();
          。。。。
     @ OneToMany (mappedBy= "order" ,cascade = CascadeType. ALL , fetch = FetchType. LAZY )
     @ OrderBy (value =  "id ASC" )
     public  Set<OrderItem> getOrderItems() {
         return orderItems ;
     }
}
 
publicclass  OrderItem  implements  Serializable {
private  Order  order ;
。。。。
     @ ManyToOne (cascade=CascadeType. REFRESH ,optional= false )
     @ JoinColumn (name =  "order_id" )
     public  Order getOrder() {
         return order ;
     }
}
@OrderBy(value = "id ASC")  指明加载 OrderItem  时按 id  的升序排序
 
@OneToMany 的属性
1>targetEntity
定义关系类的类型,默认是该成员属性对应的类类型,所以通常不需要提供定义
 
2>mappedBy
定义类之间的双向关系。如果类之间是单向关系,不需要提供定义如果类和类之间形成双向关系,我们就需要使用这个属性进行定义,否则可能引起数据一致性的问题
该属性的值是“多”方class里的“一”方的变量名
 
3>cascade
该属性定义类和类之间的级联关系。定义的级联关系将被容器视为对当前类对象及其关联类对象采取相同的操作,而且这种关系是递归调用的。举个例子: Order  OrderItem 有级联关系,那么删除 Order 时将同时删除它所对应的 OrderItem 对象。而如果 OrderItem 还和其他的对象之间有级联关系,那么这样的操作会一直递归执行下去。
 
cascade 的值只能从 CascadeType.PERSIST (级联新建)、 CascadeType.REMOVE (级联删除)、 CascadeType.REFRESH (级联刷新)、 CascadeType.MERGE (级联更新)中选择一个或多个。还有一个选择是使用 CascadeType.ALL ,表示选择全部四项。
 
4>fetch
可选择项包括: FetchType.EAGER FetchType.LAZY 。前者表示关系类 ( 本例是 OrderItem  ) 在主类 ( 本例是 Order ) 加载的时候同时加载,后者表示关系类在被访问时才加载。默认值是 FetchType.LAZY
 
@JoinColumn(name = "order_id") 注释指定OrderItem映射表的order_id列作为外键与Order 映射表的主键列关联。
 
@ManyToOne :指明OrderItem和Order之间为多对一关系。
 
@ManyToOne 注释有四个属性:targetEntity、cascade、fetch 和optional,前三个属性的具体含义和@OneToMany的同名属性相同,但@ManyToOne的fetch 属性默认值是FetchType.EAGER

@OneToMany and @ManyToMany 关系的默认FetchType  LAZY

@OneToOne and @ManyToOne 关系的默认 FetchTypeEAGER

reference为集合的fetch type Lazy, 看上去还是比较有道理的。但是如果记不清的话,可以根据自己的需要统统声明一下。毕竟这些默认设置和Hibernate是不同的。


 
optional 属性是定义该关联类是否必须存在值为false 时,关联类双方都必须存在,如果关系被维护端不存在,查询的结果为null。值为true 时, 关系被维护端可以不存在,查询的结果仍然会返回关系维护端,在关系维护端中指向关系被维护端的属性为nulloptional属性的默认值是trueoptional 属性实际上指定关联类与被关联类的join 查询关系,如optional=false 时join 查询关系为inner join, optional=true 时join 查询关系为left join。下面代码片断解释如下:
 
 
 
 
 
有一点需要强调:当业务方法需要把一个实体 Bean 作为参数返回给客户端时,除了实体 Bean 本身需要实现 Serializable  接口之外 ,如果关联类(OrderItem)是延迟加载,还需在返回实体Bean之前通过访问关联类的方式加载关联类(见下例)。否则在客户端访问关联类时将会抛出加载例外。
     public  Order getOrderByID(Integer orderid) {
        Order order =  em .find(Order. class , orderid);        
        //!!!!! 因为是延迟加载,通过执行 size() 这种方式获取订单下的所有订单项
        order.getOrderItems().size();
        return  order;
     }
 
另外不管是否延迟加载,通过join fetch 关联语句都可显式加载关联类 ,如下例:
 
     public  List getAllOrder() {
         Query query =  em .createQuery( "select DISTINCT o from Order o inner
join fetch o.orderItems order by o.orderid" );
         List result = query.getResultList();
         return  result;
     }

。。。。。。(详见原文:http://blog.csdn.net/he90227/article/details/38228467)



hibernate中基于annotation(注解)的many2many双向

简单的多对多映射关系

Admin.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package  org.zttc.itat.model;
  
import  java.util.Set;
  
import  javax.persistence.Entity;
import  javax.persistence.GeneratedValue;
import  javax.persistence.Id;
import  javax.persistence.ManyToMany;
import  javax.persistence.Table;
  
@Entity
@Table (name= "t_admin" )
public  class  Admin {
     private  int  id;
     private  String name;
     private  Set<Role> roles;
      
     @Id
     @GeneratedValue
     public  int  getId() {
         return  id;
     }
     public  void  setId( int  id) {
         this .id = id;
     }
     public  String getName() {
         return  name;
     }
     public  void  setName(String name) {
         this .name = name;
     }
     @ManyToMany (mappedBy= "admins" )
     public  Set<Role> getRoles() {
         return  roles;
     }
     public  void  setRoles(Set<Role> roles) {
         this .roles = roles;
     }
}

Role.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package  org.zttc.itat.model;
  
import  java.util.HashSet;
import  java.util.Set;
  
import  javax.persistence.Entity;
import  javax.persistence.GeneratedValue;
import  javax.persistence.Id;
import  javax.persistence.JoinColumn;
import  javax.persistence.JoinTable;
import  javax.persistence.ManyToMany;
import  javax.persistence.Table;
  
@Entity
@Table (name= "t_role" )
public  class  Role {
     private  int  id;
     private  String name;
     private  Set<Admin> admins;
      
     public  Role() {
         admins =  new  HashSet<Admin>();
     }
     public  void  add(Admin admin) {
         admins.add(admin);
     }
      
     @Id
     @GeneratedValue
     public  int  getId() {
         return  id;
     }
     public  void  setId( int  id) {
         this .id = id;
     }
     public  String getName() {
         return  name;
     }
     public  void  setName(String name) {
         this .name = name;
     }
      
     @ManyToMany
     @JoinTable (name= "t_role_admin" ,joinColumns={ @JoinColumn (name= "rid" )},
             inverseJoinColumns={ @JoinColumn (name= "aid" )})
     public  Set<Admin> getAdmins() {
         return  admins;
     }
     public  void  setAdmins(Set<Admin> admins) {
         this .admins = admins;
     }
}


原文链接:http://www.656463.com/article/zimQ7n.htm




关系/对象映射 多对多关系(@ManyToMany 注释)【重新认识】

查了好多资料

old:

@ManyToMany 注释:表示此类是多对多关系的一边,mappedBy 属性定义了此类为双向关系的维护端, 注意:mappedBy 属性的值为此关系的另一端的属性名。
例如,在Student类中有如下方法:
被控方:
             @ManyToMany(fetch = FetchType.LAZY, mappedBy = "students")
public Set<Teacher> getTeachers() {
return teachers;
}
那么这里的“students”就是Teachers的一个属性,通常应该是这样的:
Set<Student> students;
另一端的 getStudents 方法如下所示:
 主控方:
              @ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
@JoinTable(name = "Teacher_Student",
joinColumns = {@JoinColumn(name = "Teacher_ID", referencedColumnName = "teacherid")},
inverseJoinColumns = {@JoinColumn(name = "Student_ID", referencedColumnName ="studentid")})
public Set<Student> getStudents() {
return students;
}
@ManyToMany 注释表示Teacher 是多对多关系的一端。@ JoinTable 描述了多对多关系的数据表关系。name 属性指定 中间表名称,joinColumns 定义 中间表与Teacher 表的外键关系。上面的代码中, 中间表Teacher_Student的Teacher_ID 列是Teacher 表的主键列对应的外键列,inverseJoinColumns 属性定义了 中间表与另外一端(Student)的外键关系。
 
可以通过上面的定义看到有三个表学生表--老师表--老师学生中间表
以上提到主控方和被控方。。本人不赞同这种写法:
理由是: 1.既然是多对多关系。。为什么还要分主动方和被动方?
              2.为什么需要删除老师后才级联中间表。。。请注意:以上定义方法时,删除学生是无法级联删除中间表的。
正确的写法应该是两边都用主控方的写法:
只是joinColumns和inverseJoinColumns属性的地方互换就可以了
new:(个人观点,,欢迎大家一起来讨论此问题)
总结:
个人的定义:
@ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
@JoinTable(name = "Teacher_Student",
joinColumns = {@JoinColumn(name = "Student_ID", referencedColumnName = "studentid")},
inverseJoinColumns = {@JoinColumn(name = "Teacher_ID", referencedColumnName ="teacherid")})
public Set<Teacher> getTeachers() {
return teachers;
}
@ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
@ JoinTable(name = "Teacher_Student",
joinColumns = {@ JoinColumn(name = "Teacher_ID", referencedColumnName = "teacherid")},
inverseJoinColumns = {@JoinColumn(name = "Student_ID", referencedColumnName ="studentid")})
public Set<Student> getStudents() {
return students;
}


原文链接:http://blog.csdn.net/gabriel80/article/details/4260923

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值