这篇blog主要是总结一下Hibernate注解的使用.主要分为三部分,第一部分声明实体类的注解,包括声明实体类过程中的约束条件注解的使用,第二部分主要为关联关系之间的注解声明,最后一部分作为其他注解.
1.为什么要使用注解?
说实话,我觉得使用觉的使用hbm.xml映射文件能够是映射关系看起来更明确显眼些,有哪些实体类,映射到数据库中有哪些列也是比较明显的,而且我觉的,配置文件使项目的解耦更好一点(当然,也许你会说如果要更改映射关系,使不使用配置文件都是需要更改源文件的,.hbm.xml文件也是源文件,毕竟它和web.xml文件还是有一些差别的,没准不适用配置文件还只需要更改一个文件,额...想想其实也是的).不过使用配置文件有一个很明显的缺点:实体类多了之后,要在不同文件之间转换,更麻烦...所以从现在开始,来学习使用注解吧.(目前为止我们还不需要更改pom.xml文件,目前所用的所有包都已经包括进来了)
2.实体类的定义等相关注解
2.1 定义实体类
package entities;
import javax.persistence.*;
import java.util.Date;
/**
* Created by tbxsx on 17-5-30.
*/
@Entity
@Table(name = "`provice`")
public class Provice {
private long id;
private String name;
private String email;
private Date create_date;
public Provice() {
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "`id`")
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Column(name = "`name`", nullable = false, length = 30)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column(name = "`email`", nullable = false, unique = true, length = 30)
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
//注意在这里设置了属性 insertable = false,columnDefinition = "datetime default now()" 这些会在后面讲到
@Column(name = "`create_date`", insertable = false, columnDefinition = "datetime default now()", nullable = false
)
public Date getCreate_date() {
return create_date;
}
public void setCreate_date(Date create_date) {
this.create_date = create_date;
}
}
定义如上图,测试如下:
@Test
public void testAnotation(){
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
Provice provice = new Provice();
provice.setName("hunan");
provice.setEmail("somebody@email.com");
session.save(provice);
session.getTransaction().commit();
System.out.println("Test!");
}
SQL语句:
Hibernate:
create table `provice` (
`id` bigint not null auto_increment,
`create_date` datetime default now() not null,
`email` varchar(30) not null,
`name` varchar(30) not null,
primary key (`id`)
) engine=MyISAM
Hibernate:
alter table `provice`
add constraint UK_b1cb2vul5rqbel47yqidov4h3 unique (`email`)
insert
into
`
provice` (
`email`, `name`
)
values
(?, ?)
数据库结果:
注解从大到小:
1.Table层次
@Entity:实体类注解(必需)
public @interface Entity {
/**
* (Optional) The entity name. Defaults to the unqualified
* name of the entity class. This name is used to refer to the
* entity in queries. The name must not be a reserved literal
* in the Java Persistence query language.
*/
String name() default "";
}
只有一个名字属性,名字属性定义的是实体类名称(默认为类名).
@Table:
/**
* Specifies the primary table for the annotated entity. Additional
* tables may be specified using {@link SecondaryTable} or {@link
* SecondaryTables} annotation.
* <p/>
* If no <code>Table</code> annotation is specified for an entity
* class, the default values apply.
*
* <pre>
* Example:
*
* @Entity
* @Table(name="CUST", schema="RECORDS")
* public class Customer { ... }
* </pre>
*
* @since Java Persistence 1.0
*/
@Target(TYPE)
@Retention(RUNTIME)
public @interface Table {
/**
* (Optional) The name of the table.
* <p/>
* Defaults to the entity name.
*/
String name() default "";
/**
* (Optional) The catalog of the table.
* <p/>
* Defaults to the default catalog.
*/
String catalog() default "";
/**
* (Optional) The schema of the table.
* <p/>
* Defaults to the default schema for user.
*/
String schema() default "";
/**
* (Optional) Unique constraints that are to be placed on
* the table. These are only used if table generation is in
* effect. These constraints apply in addition to any constraints
* specified by the <code>Column</code> and <code>JoinColumn</code>
* annotations and constraints entailed by primary key mappings.
* <p/>
* Defaults to no additional constraints.
*/
UniqueConstraint[] uniqueConstraints() default { };
/**
* (Optional) Indexes for the table. These are only used if table generation is in effect. Defaults to no
* additional indexes.
*
* @return The indexes
*/
Index[] indexes() default {};
}
和Entity对应的是@Table注解,诠释与这个类对应的数据库表的相关属性(非必需,Hibernate会产生默认名字,默认名为实体类名,即@Entity定义的名字,如果@Entity没有定义,那么数据库映射表名即为类名)当实体类和数据库中对应表名称不同时,需要使用该注解.name属性是在数据库中表名称,schema和category是数据库名称,unqiqueConstraints设置表约束.更多
2.属性层次
主键:
@Id
声明此属性为主键。
@EmbeddedId : 组合主键
@Entity
public class Login {
@EmbeddedId
private PK pk;
@Embeddable
public static class PK implements Serializable {
private String system;
private String username;
...
}
...
}
@GeneratedValue
指定主键的生成策略。有如下四个值
TABLE:使用表保存id值,单独生成一个表 更多
IDENTITY:identitycolumn,通过auto_increment产生
SEQUENCR :sequence,需要数据库支持序列
AUTO:根据数据库的不同使用上面三个(当我使用Mysql并不确定生成策略的时候,使用了Table策略)
一般属性:
@Column
/**
* Is used to specify the mapped column for a persistent property or field.
* If no <code>Column</code> annotation is specified, the default values apply.
*
* <blockquote><pre>
* Example 1:
*
* @Column(name="DESC", nullable=false, length=512)
* public String getDescription() { return description; }
*
* Example 2:
*
* @Column(name="DESC",
* columnDefinition="CLOB NOT NULL",
* table="EMP_DETAIL")
* @Lob
* public String getDescription() { return description; }
*
* Example 3:
*
* @Column(name="ORDER_COST", updatable=false, precision=12, scale=2)
* public BigDecimal getCost() { return cost; }
*
* </pre></blockquote>
*
*
* @since Java Persistence 1.0
*/
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface Column {
/**
* (Optional) The name of the column. Defaults to
* the property or field name.
*/
String name() default "";
/**
* (Optional) Whether the column is a unique key. This is a
* shortcut for the <code>UniqueConstraint</code> annotation at the table
* level and is useful for when the unique key constraint
* corresponds to only a single column. This constraint applies
* in addition to any constraint entailed by primary key mapping and
* to constraints specified at the table level.
*/
boolean unique() default false;
/**
* (Optional) Whether the database column is nullable.
*/
boolean nullable() default true;
/**
* (Optional) Whether the column is included in SQL INSERT
* statements generated by the persistence provider.
*/
boolean insertable() default true;
/**
* (Optional) Whether the column is included in SQL UPDATE
* statements generated by the persistence provider.
*/
boolean updatable() default true;
/**
* (Optional) The SQL fragment that is used when
* generating the DDL for the column.
* <p> Defaults to the generated SQL to create a
* column of the inferred type.
*/
String columnDefinition() default "";
/**
* (Optional) The name of the table that contains the column.
* If absent the column is assumed to be in the primary table.
*/
String table() default "";
/**
* (Optional) The column length. (Applies only if a
* string-valued column is used.)
*/
int length() default 255;
/**
* (Optional) The precision for a decimal (exact numeric)
* column. (Applies only if a decimal column is used.)
* Value must be set by developer if used when generating
* the DDL for the column.
*/
int precision() default 0;
/**
* (Optional) The scale for a decimal (exact numeric) column.
* (Applies only if a decimal column is used.)
*/
int scale() default 0;
}
可在此定义相关约束条件.
主要讲一下columnDefinition 和 insertable,updatable.
比如说需要定义时间的默认值,默认值为插入数据库的时候.那么怎么办呢?通过设置 columnDefinition,这句话会在生成DDL时候,加入DDL中.如:
@Column(name = "`create_date`", insertable = false, columnDefinition = "datetime default now()", nullable = false
)
public Date getCreate_date() {
return create_date;
}
生成的DDL为:
`create_date` datetime default now() not null,
直接加载`create_date`后面.
然后设置
insertable = false
这样在save的时候产生的语句为:
insert
into
`
provice` (
`email`, `name`
)
values
(?, ?)
否则为:
insert
into
`
provice` (
`email`, `name`,`create_date`
)
values
(?, ?, ?)
如果为这种情况,因为create_date是数据库自动生成的,在实体类中,create_date为null,所以插入的create_date将为null.
此外,如果想将create_date设置为对象生成时候的值,可以:
private Date create_date = new Date()
3.对象关联关系的注解
首先介绍一下注解:@JoinColumn
我们知道在两个表之间通过外键来表示关联关系.
@JoinColumn 可以注释本表中指向另一个表的外键,相当与一个外键的定义.
public @interface JoinColumn {
/**
* (Optional) The name of the foreign key column.
* The table in which it is found depends upon the
* context.
* <ul>
* <li>If the join is for a OneToOne or ManyToOne
* mapping using a foreign key mapping strategy,
* the foreign key column is in the table of the
* source entity or embeddable.
* <li> If the join is for a unidirectional OneToMany mapping
* using a foreign key mapping strategy, the foreign key is in the
* table of the target entity.
* <li> If the join is for a ManyToMany mapping or for a OneToOne
* or bidirectional ManyToOne/OneToMany mapping using a join
* table, the foreign key is in a join table.
* <li> If the join is for an element collection, the foreign
* key is in a collection table.
*</ul>
*
* <p> Default (only applies if a single join column is used):
* The concatenation of the following: the name of the
* referencing relationship property or field of the referencing
* entity or embeddable class; "_"; the name of the referenced
* primary key column.
* If there is no such referencing relationship property or
* field in the entity, or if the join is for an element collection,
* the join column name is formed as the
* concatenation of the following: the name of the entity; "_";
* the name of the referenced primary key column.
*/
String name() default "";
/**
* (Optional) The name of the column referenced by this foreign
* key column.
* <ul>
* <li> When used with entity relationship mappings other
* than the cases described here, the referenced column is in the
* table of the target entity.
* <li> When used with a unidirectional OneToMany foreign key
* mapping, the referenced column is in the table of the source
* entity.
* <li> When used inside a <code>JoinTable</code> annotation,
* the referenced key column is in the entity table of the owning
* entity, or inverse entity if the join is part of the inverse
* join definition.
* <li> When used in a <code>CollectionTable</code> mapping, the
* referenced column is in the table of the entity containing the
* collection.
* </ul>
*
* <p> Default (only applies if single join column is being
* used): The same name as the primary key column of the
* referenced table.
*/
String referencedColumnName() default "";
/**
* (Optional) Whether the property is a unique key. This is a
* shortcut for the <code>UniqueConstraint</code> annotation at
* the table level and is useful for when the unique key
* constraint is only a single field. It is not necessary to
* explicitly specify this for a join column that corresponds to a
* primary key that is part of a foreign key.
*/
boolean unique() default false;
/** (Optional) Whether the foreign key column is nullable. */
boolean nullable() default true;
/**
* (Optional) Whether the column is included in
* SQL INSERT statements generated by the persistence
* provider.
*/
boolean insertable() default true;
/**
* (Optional) Whether the column is included in
* SQL UPDATE statements generated by the persistence
* provider.
*/
boolean updatable() default true;
/**
* (Optional) The SQL fragment that is used when
* generating the DDL for the column.
* <p> Defaults to the generated SQL for the column.
*/
String columnDefinition() default "";
/**
* (Optional) The name of the table that contains
* the column. If a table is not specified, the column
* is assumed to be in the primary table of the
* applicable entity.
*
* <p> Default:
* <ul>
* <li> If the join is for a OneToOne or ManyToOne mapping
* using a foreign key mapping strategy, the name of the table of
* the source entity or embeddable.
* <li> If the join is for a unidirectional OneToMany mapping
* using a foreign key mapping strategy, the name of the table of
* the target entity.
* <li> If the join is for a ManyToMany mapping or
* for a OneToOne or bidirectional ManyToOne/OneToMany mapping
* using a join table, the name of the join table.
* <li> If the join is for an element collection, the name of the collection table.
* </ul>
*/
String table() default "";
/**
* (Optional) The foreign key constraint specification for the join column. This is used only if table generation
* is in effect. Default is provider defined.
*
* @return The foreign key specification
*/
ForeignKey foreignKey() default @ForeignKey();
}
3.1 @OneToOne
配合@JoinColumn使用
我们现在使用Husband和Wife的一对一关系演示:
@Entity
public class Husband {
private long id;
private String name;
private Wife wife;
public Husband() {
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.PERSIST)
@JoinColumn(name = "wife_id")
public Wife getWife() {
return wife;
}
public void setWife(Wife wife) {
this.wife = wife;
}
}
@Entity
public class Wife {
private long id;
private String name;
private Husband husband;
public Wife() {
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.PERSIST)
@JoinColumn(name = "husband_id")
public Husband getHusband() {
return husband;
}
public void setHusband(Husband husband) {
this.husband = husband;
}
}
@Test
public void testOneToOneAnotation(){
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
Husband husband = session.get(Husband.class,1l);
session.delete(husband);
session.getTransaction().commit();
System.out.println("Test!");
}
注意点:
1.@JoinColumn 和@OneToOne中MappedBy的用法.
前者可以理解为在Wife表中建立一个代表Husband的属性,当然这是一个外键.
后者代表:Husband 在Wife实体类中有一个指向(即Wife类中有一个名字为husband的Husband对象的引用).即mappedBy标签定义在被拥有方的,他指向拥有方;
mappedBy跟joinColumn/JoinTable总是处于互斥的一方,可以理解为正是由于拥有方的关联被拥有方的字段存在,拥有方才拥有了被拥有方。mappedBy这方定义JoinColumn/JoinTable总是失效的,不会建立对应的字段或者表。
参考
参考
2. Cascade 的使用方法.
Cascade:
CascadeType.REFRESH:级联刷新,当多个用户同时作操作一个实体,为了用户取到的数据是实时的,在用实体中的数据之前就可以调用一下refresh()方法!
CascadeType.REMOVE:级联删除,当调用remove()方法删除Husband实体时会先级联删除wife的相关数据!
CascadeType.MERGE:级联更新,当调用了Merge()方法,如果husband中的数据改变了会相应的更新OWife中的数据,
CascadeType.ALL:包含以上所有级联属性。
(注:以上几种级联操作,只能实在满足数据库的约束时才能生效)
CascadeType.PERSIST:级联保存,当调用了Persist() 方法,会级联保存相应的数据
比如说:举例CascadeType.PERSIST.
如果前面的例子中,没有Cascade属性,使用save方法保存Hubband的时候,将会由于它的外键wife没有保存,导致出现错误(外键不存在),如果使用了该属性,那么它会保存两个实体类,消除错误.
ERROR: HHH000346: Error during managed flush [org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance beforeQuery flushing: entities.Wife]
java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance beforeQuery flushing: entities.Wife
参考
3. orphanRemoval 的使用方法
在这里,如果只设置cascade为persist,不设置该属性的时候,删除一个husband,将不会删除对应的wife,如果该属性为true,将会删除两者.
更多的该属性用法会出现在多对多或者一对多关系中,如果有中间关系表,删除实体的时候,该属性会使hibernate同时删除中间表的记录.
3.2 @ManyToOne
3.3 @OneToMany
多方:
@ManyToOne
一方:
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)
3.4 @ManyToMany
主方拥有者:@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
从拥有者方:@ManyToMany(mappedBy = "addresses")
3.5 @Any
3.6 @ManyToAny