HIbernate注解说明
1、@Entity(name="EntityName")
必须,name为可选,对应数据库中一的个表,此处的EntryName要注意,在代码中使用hql进行查询时,需要from Xxx,那么Xxx就是对应的此处的EntryName,所以要注意EntryName的命名,否则会出现映射问题。
2、@Table(name="",catalog="",schema="")
可选,通常和@Entity配合使用,只能标注在实体的class定义处,表示实体对应的数据库表的信息
name:可选,表示表的名称。默认地,表名和实体名称一致,只有在不一致的情况下才需要指定表名
catalog:可选,表示Catalog名称。默认为Catalog("")
schema:可选,表示Schema名称。默认为Schema("")
3、@id
必须
@id定义了映射到数据库表的主键的属性,一个实体只能有一个属性被映射为主键。置于getXxxx()前
4、@GeneratedValue(strategy=GenerationType,generator="")
可选
strategy:表示主键生成策略,有AUTO、INDENTITY、SEQUENCE和 TABLE 4种,分别表示让ORM框架自动选择、根据数据库的Identity字段生成、根据数据库表的Sequence字段生成、以及根据一个额外的表生成主键。默认为AUTO
generator:表示主键生成器的名称,这个属性通常和ORM框架相关。例如,Hibernate可以指定uuid等主键生成方式
示例:
@Id
@GeneratedValues(strategy=StrategyType.SEQUENCE)
public int getPk() {
return pk;
}
5、@Basic(fetch=FetchType,optional=true)
可选
@Basic表示一个简单的属性到数据库表的字段的映射,对于没有任何标注的getXxxx()方法。默认即为@Basic
fetch:表示该属性的读取策略,有EAGER和LAZY两种,分别表示主支抓取和延迟加载。默认为EAGER
optional:表示该属性是否允许为null,默认为true
示例:
@Basic(optional=false)
public String getAddress() {
return address;
}
6、@Column
可选
@Column描述了数据库表中该字段的详细定义,这对于根据JPA注解生成数据库表结构的工具非常有作用
name:表示数据库表中该字段的名称。默认情形属性名称一致
nullable:表示该字段是否允许为null。默认为true
unique:表示该字段是否是唯一标识。默认为false
length:表示该字段的大小,仅对String类型的字段有效
insertable:表示在ORM框架执行插入操作时,该字段是否应出现INSETRT语句中。默认为true
updateable:表示在ORM框架执行更新操作时,该字段是否应该出现在UPDATE语句中,默认为true。对于一经创建就不可以更改的字段,该属性非常有用,如对于birthday字段
columnDefinition:表示该字段在数据库中的实际类型。通常ORM框架可以根据属性类型自动判断数据库中字段的类型,但是对于Date类型仍无法确定数据库中字段类型究竟是DATE、TIME还是TIMESTAMP。此外,String的默认映射类型为VARCHAR,如果要将String类型映射到特定数据库的BLOB或TEXT字段类型,该属性非常有用
示例:
@Column(name="BIRTH",nullable="false",columnDefinition="DATE")
public String getBithday() {
return birthday;
}
7、@Transient
可选
@Transient表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性
如果一个属性并非数据库表的字段映射,就务必将其标示为@Transient,否则,ORM框架默认其注解为@Basic
示例:
//根据birth计算出age属性
@Transient
public int getAge() {
return getYear(new Date()) -getYear(birth);
}
8、@ManyToOne(fetch=FetchType,cascade=CascadeType)
可选
@ManyToOne表示一个多对一的映射,该注解标注的属性通常是数据库表的外键
optional:是否允许该字段为null,该属性应该根据数据库表的外键约束来确定,默认为true
fetch:表示抓取策略,默认为FetchType.EAGER
cascade:表示默认的级联操作策略,可以指定为ALL、PERSIST、MERGE、REFRESH和REMOVE中的若干组合,默认为无级联操作
targetEntity:表示该属性关联的实体类型,该属性通常不必指定,ORM框架根据属性类型自动判断targetEntity
示例:
//订单Order和用户User是一个ManyToOne的关系
//在Order类中定义
@ManyToOne()
@JoinColumn(name="USER")
public User getUser() {
return user;
}
9、@JoinColumn
可选
@JoinColumn和@Column类似,介量描述的不是一个简单字段,而是一个关联字段,例如,描述一个@ManyToOne的字段.
name:该字段的名称,由于@JoinColumn描述的是一个关联字段,如ManyToOne,则默认的名称由其关联的实体决定.
例如,实体Order有一个user属性来关联实体User,则Order的user属性为一个外键
其默认的名称为实体User的名称+下划线+实体User的主键名称
示例:
见@ManyToOne
10、@OneToMany(fetch=FetchType,cascade=CascadeType)
可选
@OneToMany描述一个一对多的关联,该属性应该为集体类型,在数据库中并没有实际字段
fetch:表示抓取策略,默认为FetchType.LAZY,因为关联的多个对象通常不必从数据库预先读取到内存
cascade:表示级联操作策略,对于OneToMany类型的关联非常重要,通常该实体更新或删除时,其关联的实体也应当被更新或删除
例如:实体User和Order是OneToMany的关系,则实体User被删除时,其关联的实体Order也应该被全部删除
示例:
@OneTyMany(cascade=ALL)
public List getOrders() {
return orders;
}
11、@OneToOne(fetch=FetchType,cascade=CascadeType)
可选
@OneToOne描述一个一对一的关联
fetch:表示抓取策略,默认为FetchType.LAZY
cascade:表示级联操作策略
示例:
@OneToOne(fetch=FetchType.LAZY)
public Blog getBlog() {
return blog;
}
12、@ManyToMany
可选
@ManyToMany描述一个多对多的关联,多对多关联上是两个一对多关联,但是在ManyToMany描述中,中间表是由ORM框架自动处理
targetEntity:表示多对多关联的另一个实体类的全名,例如:package.Book.class
mappedBy:表示多对多关联的另一个实体类的对应集合属性名称
13、@MappedSuperclass
可选
@MappedSuperclass可以将超类的JPA注解传递给子类,使子类能够继承超类的JPA注解
示例:
@MappedSuperclass
public class Employee() {
....
}
@Entity
public class Engineer extends Employee {
.....
}
@Entity
public class Manager extends Employee {
.....
}
14、@Embedded
可选
@Embedded将几个字段组合成一个类,并作为整个Entity的一个属性
例如User包括id、name、city、street、zip属性
我们希望city、street、zip属性映射为Address对象。这样,User对象将具有id、name和address这三个属性.
Address对象必须定义为@Embededable
示例:
@Embeddable
public class Address {city,street,zip}
@Entity
public class User {
@Embedded
public Address getAddress() {
..........
}
}
1、Hibernate注解关系处理关系映射
在hibernate中,通常配置对象关系映射关系有两种,一种是基于xml的方式,另一种是基于annotation的注解方式,熟话说,萝卜青菜,可有所爱,每个人都有自己喜欢的配置方式,我在试了这两种方式以后,发现使用annotation的方式可以更简介,所以这里就简单记录下通过annotation来配置各种映射关系,在hibernate4以后已经将annotation的jar包集成进来了,如果使用hibernate3的版本就需要引入annotation的jar包。
一、单对象操作
@Entity ---> 如果我们当前这个bean要设置成实体对象,就需要加上Entity这个注解
@Table(name="t_user") ----> 设置数据库的表名
public class User
{
private int id;
private String username;
private String password;
private Date born;
private Date registerDate;
@Column(name="register_date") ---> Column中的name属性对应了数据库的该字段名字,里面还有其他属性,例如length,nullable等等
public Date getRegisterDate()
{
return registerDate;
}
public void setRegisterDate(Date registerDate)
{
this.registerDate = registerDate;
}
@Id ---> 定义为数据库的主键ID (建议不要在属性上引入注解,因为属性是private的,如果引入注解会破坏其封装特性,所以建议在getter方法上加入注解)
@GeneratedValue ----> ID的生成策略为自动生成
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
............
}
最后只需要在hibernate.cfg.xml文件里面将该实体类加进去即可:
<!-- 基于annotation的配置 -->
<mapping class="com.xiaoluo.bean.User"/>
<!-- 基于hbm.xml配置文件 -->
<mapping resource="com/xiaoluo/bean/User.hbm.xml"/>
这样我们就可以写测试类来进行我们的CRUD操作了。
二、一对多的映射(one-to-many)
这里我们定义了两个实体类,一个是ClassRoom,一个是Student,这两者是一对多的关联关系。
ClassRoom类:
@Entity
@Table(name="t_classroom")
public class ClassRoom
{
private int id;
private String className;
private Set<Student> students;
public ClassRoom()
{
students = new HashSet<Student>();
}
public void addStudent(Student student)
{
students.add(student);
}
@Id
@GeneratedValue
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getClassName()
{
return className;
}
public void setClassName(String className)
{
this.className = className;
}
@OneToMany(mappedBy="room") ---> OneToMany指定了一对多的关系,mappedBy="room"指定了由多的那一方来维护关联关系,mappedBy指的是多的一方对1的这一方的依赖的属性,(注意:如果没有指定由谁来维护关联关系,则系统会给我们创建一张中间表)
@LazyCollection(LazyCollectionOption.EXTRA) ---> LazyCollection属性设置成EXTRA指定了当如果查询数据的个数时候,只会发出一条 count(*)的语句,提高性能
public Set<Student> getStudents()
{
return students;
}
public void setStudents(Set<Student> students)
{
this.students = students;
}
}
Student类:
@Entity
@Table(name="t_student")
public class Student
{
private int id;
private String name;
private int age;
private ClassRoom room;
@ManyToOne(fetch=FetchType.LAZY) ---> ManyToOne指定了多对一的关系,fetch=FetchType.LAZY属性表示在多的那一方通过延迟加载的方式加载对象(默认不是延迟加载)
@JoinColumn(name="rid") ---> 通过 JoinColumn 的name属性指定了外键的名称 rid (注意:如果我们不通过JoinColum来指定外键的名称,系统会给我们声明一个名称)
public ClassRoom getRoom()
{
return room;
}
public void setRoom(ClassRoom room)
{
this.room = room;
}
@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;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
}
三、一对一映射(One-to-One)
一对一关系这里定义了一个Person对象以及一个IDCard对象
Person类:
@Entity
@Table(name="t_person")
public class Person
{
private int id;
private String name;
private IDCard card;
@OneToOne(mappedBy="person") ---> 指定了OneToOne的关联关系,mappedBy同样指定由对方来进行维护关联关系
public IDCard getCard()
{
return card;
}
public void setCard(IDCard card)
{
this.card = card;
}
@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;
}
}
IDCard类:
@Entity
@Table(name="t_id_card")
public class IDCard
{
private int id;
private String no;
private Person person;
@Id
@GeneratedValue
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getNo()
{
return no;
}
public void setNo(String no)
{
this.no = no;
}
@OneToOne ---> OnetoOne指定了一对一的关联关系,一对一中随便指定一方来维护映射关系,这里选择IDCard来进行维护
@JoinColumn(name="pid") ---> 指定外键的名字 pid
public Person getPerson()
{
return person;
}
public void setPerson(Person person)
{
this.person = person;
}
}
注意:在判断到底是谁维护关联关系时,可以通过查看外键,哪个实体类定义了外键,哪个类就负责维护关联关系。
四、Many-to-Many映射(多对多映射关系)
多对多这里通常有两种处理方式,一种是通过建立一张中间表,然后由任一一个多的一方来维护关联关系,另一种就是将多对多拆分成两个一对多的关联关系
1.通过中间表由任一一个多的一方来维护关联关系
Teacher类:
@Entity
@Table(name="t_teacher")
public class Teacher
{
private int id;
private String name;
private Set<Course> courses;
public Teacher()
{
courses = new HashSet<Course>();
}
public void addCourse(Course course)
{
courses.add(course);
}
@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="teachers") ---> 表示由Course那一方来进行维护
public Set<Course> getCourses()
{
return courses;
}
public void setCourses(Set<Course> courses)
{
this.courses = courses;
}
}
Course类:
@Entity
@Table(name="t_course")
public class Course
{
private int id;
private String name;
private Set<Teacher> teachers;
public Course()
{
teachers = new HashSet<Teacher>();
}
public void addTeacher(Teacher teacher)
{
teachers.add(teacher);
}
@ManyToMany ---> ManyToMany指定多对多的关联关系
@JoinTable(name="t_teacher_course", joinColumns={ @JoinColumn(name="cid")},
inverseJoinColumns={ @JoinColumn(name = "tid") }) ---> 因为多对多之间会通过一张中间表来维护两表直接的关系,所以通过 JoinTable 这个注解来声明,name就是指定了中间表的名字,JoinColumns是一个 @JoinColumn类型的数组,表示的是我这方在对方中的外键名称,我方是Course,所以在对方外键的名称就是 rid,inverseJoinColumns也是一个 @JoinColumn类型的数组,表示的是对方在我这放中的外键名称,对方是Teacher,所以在我方外键的名称就是 tid
public Set<Teacher> getTeachers()
{
return teachers;
}
public void setTeachers(Set<Teacher> teachers)
{
this.teachers = teachers;
}
@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;
}
}
2.将Many-to-Many拆分成两个One-to-Many的映射(Admin、Role、AdminRole)
Admin类:
@Entity
@Table(name="t_admin")
public class Admin
{
private int id;
private String name;
private Set<AdminRole> ars;
public Admin()
{
ars = new HashSet<AdminRole>();
}
public void add(AdminRole ar)
{
ars.add(ar);
}
@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;
}
@OneToMany(mappedBy="admin") ---> OneToMany关联到了AdminRole这个类,由AdminRole这个类来维护多对一的关系,mappedBy="admin"
@LazyCollection(LazyCollectionOption.EXTRA)
public Set<AdminRole> getArs()
{
return ars;
}
public void setArs(Set<AdminRole> ars)
{
this.ars = ars;
}
}
Role类:
@Entity
@Table(name="t_role")
public class Role
{
private int id;
private String name;
private Set<AdminRole> ars;
public Role()
{
ars = new HashSet<AdminRole>();
}
public void add(AdminRole ar)
{
ars.add(ar);
}
@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;
}
@OneToMany(mappedBy="role") ---> OneToMany指定了由AdminRole这个类来维护多对一的关联关系,mappedBy="role"
@LazyCollection(LazyCollectionOption.EXTRA)
public Set<AdminRole> getArs()
{
return ars;
}
public void setArs(Set<AdminRole> ars)
{
this.ars = ars;
}
}
AdminRole类:
@Entity
@Table(name="t_admin_role")
public class AdminRole
{
private int id;
private String name;
private Admin admin;
private Role role;
@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;
}
@ManyToOne ---> ManyToOne关联到Admin
@JoinColumn(name="aid")
public Admin getAdmin()
{
return admin;
}
public void setAdmin(Admin admin)
{
this.admin = admin;
}
@ManyToOne --->
@JoinColumn(name="rid")
public Role getRole()
{
return role;
}
public void setRole(Role role)
{
this.role = role;
}
}
小技巧:通过hibernate来进行插入操作的时候,不管是一对多、一对一还是多对多,都只需要记住一点,在哪个实体类声明了外键,就由哪个类来维护关系,在保存数据时,总是先保存的是没有维护关联关系的那一方的数据,后保存维护了关联关系的那一方的数据,如:
Person p = new Person();
p.setName("xiaoluo");
session.save(p);
IDCard card = new IDCard();
card.setNo("1111111111");
card.setPerson(p);
session.save(card);
2、Hibernate基于注解的双向one-to-many映射关系的实现
项目中用到了一对多的实体类关系映射,之前接触的都是基于配置文件的映射实现,但是公司的大部分都是基于注解的,因此自己参考之前的代码捣鼓了基于注解的一对多的映射关系实现。
背景:
一的一端:QingAoCenterInfo:青奥场所信息,
多的一端:QingAoPlaceInfo:青奥场馆信息,
其中一个青奥场所下可以包含多个青奥场馆
one端:QingAoCenterInfo,持有QingAoPlaceInfo的List引用,
通过注解@OneToMany(mappedBy="qingAoCenterInfo",cascade= CascadeType.ALL)
mappedBy:定义类之间的双向关系。如果类之间是单向关系,不需要提供定义,如果类和类之间形成双向关系,我们就需要使用这个属性进行定义, 否则可能引起数据一致性的问题。要由One的一方指向Many的一方,并且,这个属性应该等于Many的一方中含有One类的属性的属性名,否则会出错啦
cascade:CascadeType[]类型。该属性定义类和类之间的级联关系。
定义的级联关系将被容器视为对当前类对象及其关联类对象采取相同的操作,而且这种关系是递归调用的。
举个例子:Order 和OrderItem有级联关系,那么删除QingAoCenterInfo时将同时删除它所对应的QingAoPlaceInfo对象。而如果QingAoPlaceInfo还和其他的对象之间有级联关系,那么这样的操作会一直递归执行下去。
cascade的值只能从CascadeType.PERSIST(级联新建)、CascadeType.REMOVE(级联删除)、CascadeType.REFRESH(级联刷新)、CascadeType.MERGE(级联更新)中选择一个或多个。还有一个选择是使用CascadeType.ALL,表示选择全部四项。
- package com.yuqiaotech.nttelcom.model;
- import java.util.Date;
- import java.util.List;
- import javax.persistence.CascadeType;
- import javax.persistence.Entity;
- import javax.persistence.GeneratedValue;
- import javax.persistence.GenerationType;
- import javax.persistence.Id;
- import javax.persistence.OneToMany;
- import javax.persistence.Table;
- /**
- * 青奥重点场所信息表。
- *
- */
- @Entity(name="QING_AO_CENTER_INFO")
- @Table(name="QING_AO_CENTER_INFO")
- public class QingAoCenterInfo {
- private Long id;
- private String centerName; //重点场所名称
- private Long alarmNum; //告警数
- private String note; //备注
- private String iconName; //图标名称
- private String cityName; //所在城市
- private String type; //重点场所、活动保障
- private Date createTime;
- private List<QingAoPlaceInfo> qingAoPlaceInfo; //场所拥有的场馆
- @Id
- @GeneratedValue(strategy=GenerationType.AUTO)
- public Long getId() {
- return id;
- }
- public void setId(Long id) {
- this.id = id;
- }
- /**
- * @searchItem
- * displayType="text"
- *
- * 重点场所名称
- * @return
- */
- public String getCenterName() {
- return centerName;
- }
- public void setCenterName(String centerName) {
- this.centerName = centerName;
- }
- /**
- * 告警数
- * @return
- */
- public Long getAlarmNum() {
- return alarmNum;
- }
- public void setAlarmNum(Long alarmNum) {
- this.alarmNum = alarmNum;
- }
- /**
- * 备注
- * @return
- */
- public String getNote() {
- return note;
- }
- public void setNote(String note) {
- this.note = note;
- }
- /**
- * 图标名称
- * @return
- */
- public String getIconName() {
- return iconName;
- }
- public void setIconName(String iconName) {
- this.iconName = iconName;
- }
- public String getCityName() {
- return cityName;
- }
- public void setCityName(String cityName) {
- this.cityName = cityName;
- }
- public String getType() {
- return type;
- }
- public void setType(String type) {
- this.type = type;
- }
- public Date getCreateTime() {
- return createTime;
- }
- public void setCreateTime(Date createTime) {
- this.createTime = createTime;
- }
- @OneToMany(mappedBy="qingAoCenterInfo",cascade= CascadeType.ALL)
- public List<QingAoPlaceInfo> getQingAoPlaceInfo() {
- return qingAoPlaceInfo;
- }
- public void setQingAoPlaceInfo(List<QingAoPlaceInfo> qingAoPlaceInfo) {
- this.qingAoPlaceInfo = qingAoPlaceInfo;
- }
- }
many端:QingAoPlaceInfo,持有QingAoCenterInfo的引用
通过@ManyToOne(fetch=FetchType.LAZY ) @JoinColumn(name="f_center_id")设置关联关系
@ManyToOne指明QingAoPlaceInfo和QingAoCenterInfo之间为多对一关系,多个QingAoPlaceInfo实例关联的都是同一个QingAoCenterInfo对象
fetch和lazy是用来定义级联查询的方式:
fetch:官方文档里对fetch有如下描述,Hibernate3 定义了如下几种抓取策略:
-
连接抓取(Join fetching):Hibernate 通过在
SELECT
语句使用OUTER JOIN
(外连接)来获得对象的关联实例或者关联集合。 -
批量抓取(Batch fetching):对查询抓取的优化方案,通过指定一个主键或外键列表,Hibernate 使用单条
SELECT
语句获取一批对象实例或集合。
- package com.yuqiaotech.nttelcom.model;
- import java.util.Date;
- import java.util.List;
- import javax.persistence.CascadeType;
- import javax.persistence.Entity;
- import javax.persistence.FetchType;