持久化是位于JDBC之上的一个更高层抽象。持久层将对象映射到数据库,以便在查询、装载、更新或删除对象的时候,无须使用像JDBC那样繁琐的API。EJB的早期版本中,持久化是EJB平台的一部分。EJB3.0开始,持久化已经自成规范,被称为Java Persistence API。
Java Persistence API定义了一种定义,可以将常规的普通Java对象(有时被称作POJO)映射到数据库。这些普通Java对象被称作Entity Bean。除了是用Java Persistence元数据将其映射到数据库外,Entity Bean与其他Java类没有任何区别。事实上,创建一个Entity Bean对象相当于新建一条记录,删除一个Entity Bean会同时从数据库中删除对应记录,修改一个Entity Bean时,容器会自动将Entity Bean的状态和数据库同步。
Java Persistence API还定义了一种查询语言(JPQL),具有与SQL相类似的特征,只不过做了裁减,以便处理Java对象而非原始的关系表。
59.1、持久化persistence.xml配置文件
一个实体Bean应用由实体类和persistence.xml文件组成。persistence.xml文件在jar文件的META-INF目录。persistence.xml文件指定实体Bean使用的数据源及EntityManager对象的默认行为。persistence.xml文件的配置说明如下:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="test" transaction-type="JTA">
<jta-data-source>java:/user</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"/>
</properties>
</persistence-unit>
</persistence>
persistence-unit节点可以有一个或多个,每个persistence-unit节点定义了持久化内容名称、使用的数据源及持久化产品专有属性。name属性定义持久化名称。jta-data-source节点指定实体Bean使用的数据源JNDI名称,如果应用发布在JBoss下数据源名称带有java:/前缀,数据源名称大小写敏感。properties节点用作指定持久化产品的各项属性,各个应用服务器使用的持久化产品都不一样,如JBoss使用Hibernate,WebLogic10使用Kodo。
59.2、JBoss数据源的配置
各种数据库德数据源配置模版可以在[JBoss安装目录]\docs\examples\jca目录中找到,默认名称为:数据库名+-ds.xml。
不管使用哪种数据库都需要把它的驱动类jar包放置在[JBoss安装目录]\server\default\lib目录下,放置后需要启动JBoss服务器。
数据源文件配置好后需要放置在[JBoss安装目录]/server/default/deploy目录。
SQL Server配置代码:
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>user</jndi-name>
<connection-url>jdbc:sqlserver://localhost:1433;DatabaseName=rep</connection-url>
<driver-class>com.microsoft.sqlserver.jdbc.SQLServerDriver</driver-class>
<user-name>sa</user-name>
<password>123</password>
<metadata>
<type-mapping>MS SQLSERVER2000</type-mapping>
</metadata>
</local-tx-datasource>
</datasources>
数据源发布后,可以在http://localhost:8080/jmx-console/找到配置,如下图:
59.3、单表映射的实体Bean
59.3.1、实体Bean代码
@Entity
@Table(name="tbl_user")
publicclass User implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="id")
private Integer id;
@Column(name="name")
private String name;
@Column(name="age")
private String age;
public String getAge() { returnage; }
publicvoid setAge(String age) { this.age = age; }
public String getName() { returnname;
publicvoid setName(String name) { this.name = name; }
public User() { }
public Integer getId() { returnthis.id; }
publicvoid setId(Integer id) { this.id= id; }
从上面代码来看开发实体Bean非常简单,比起普通的JavaBean就是多了些注释。@Entity注释指名这是一个实体Bean,@Table注释指定了Entity所要映射带数据库表,其中@Table.name()用来指定映射表的表名。如果缺省@Table注释,系统默认采用类名作为映射表的表名。实体Bean的每个实例代表数据表中的一行数据,行中的一列对应实例中的一个属性。
@Column注释定义了将成员属性映射到关系表中的哪一列和该列的结构信息,属性如下:
1)name:映射的列名。如:映射tbl_user表的name列,可以在name属性的上面或getName方法上面加入;
2)unique:是否唯一;
3)nullable:是否允许为空;
4)length:对于字符型列,length属性指定列的最大字符长度;
5)insertable:是否允许插入;
6)updatetable:是否允许更新;
7)columnDefinition:定义建表时创建此列的DDL;
8)secondaryTable:从表名。如果此列不建在主表上(默认是主表),该属性定义该列所在从表的名字。
@Id注释指定表的主键,它可以有多种生成方式:
1)TABLE:容器指定用底层的数据表确保唯一;
2)SEQUENCE:使用数据库德SEQUENCE列莱保证唯一(Oracle数据库通过序列来生成唯一ID);
3)IDENTITY:使用数据库的IDENTITY列莱保证唯一;
4)AUTO:由容器挑选一个合适的方式来保证唯一;
5)NONE:容器不负责主键的生成,由程序来完成。
@GeneratedValue注释定义了标识字段生成方式。
@Temporal注释用来指定java.util.Date或java.util.Calender属性与数据库类型date、time或timestamp中的那一种类型进行映射。
@Temporal(value=TemporalType.TIME)
private Date birthday;
59.3.2、会话Bean代码
@Stateless
publicclass UserDAO implements UserDAORemote {
@PersistenceContext(unitName="test")
private EntityManager em;
publicboolean insertUser(User user){
try{
em.persist(user);
returntrue
}catch(Exception e){
returnfalse;
}
}
}
上面使用了一个对象:EntityManager,EntityManager是由EJB容器自动地管理和配置的,不需要用户自己创建,它用作操作实体Bean。
如果persistence.xml文件中配置了多个不同的持久化内容。在注入EntityManager对象时必须指定持久化名称,可以通过@PersistenceContext注释的unitName属性进行制定。例如:
@PersistenceContext(unitName="test")
private EntityManager em;
59.4、属性映射
如果不想让一些成员属性映射成数据库字段,可以使用@Transient注释进行标注。
@Transient
private String sex;
如果想映射枚举对象到数据库就需要使用@Enumerated注释进行标注。
@Enumerated(EnumType.STRING)
@Column(name="address_type")
private AddressType type;//地址类型
有时可能需要存放一些文本或大文本数据进数据库,JDBC使用java.sql.Blob类型存放二进制数据,java.sql.Clob类型存放字符数据,这些数据都是非常占内存的,@Lob注释用作映射这些大数据类型,当属性的类型为byte[],Byte[]或java.io.Serializable时,@Lob注释映射为数据库的Blob类型,当属性的类型为char[],Character[]或java.lang.String时,@Lob注释将映射为数据库的Clob类型。
对于加了@Lob注释的大数据类型,为了避免每次加载实体时占用大量内存,有必要对该属性进行延时加载,这是需要用到@Basic注释。@Basic注释的定义:FetchType属性指定是否延时加载,默认为立即加载,optional属性指定在生成数据库结构时字段能否为null。
@Lob
@Basic(fetch=FetchType.LAZY)
@Column(name="info")
private String content;
59.5、持久化实体管理器EntityManager
EntityManager是用来对实体Bean进行操作的辅助类。可以用来产生/删除持久化的实体Bean,通过主键查找实体Bean,也可以通过EJB3QL语言查找满足条件的实体Bean。实体Bean被EntityManager管理时,EntityManager跟踪他的状态改变,在任何决定更新实体Bean的时候便会把发生改变的值同步到数据库中。当实体Bean从EntityManager分离后,他是不受管理的,EntityManager无法跟踪他的任何状态改变。
59.5.1、Entity获取find()或getReference()
如果知道Entity的唯一标识符,可以用find()或getReference()方法获取Entity。
public User findUser(Integer userid){
returnem.find(User.class, userid);
}
当在数据库中没有找到记录时,getReference()和find()是有区别的,find()方法会返回null,而getReference()方法会抛出javax.persistence.EntityNotFoundException例外,另外getReference()方法不保证实体Bean已被初始化。如果传递进getReference()或find()方法的参数不是实体Bean,都会引发IllegalArgumentException。
59.5.2、添加persist()
@PersistenceContext(unitName="test")
private EntityManager em;
publicboolean insertUser(User user){
try{
em.persist(user);
returntrue;
}catch(Exception e){
returnfalse;
}
}
如果传递进persist()方法的参数不是实体Bean,都会引发IllegalArgumentException例外。
59.5.3、更新实体
当实体正在被容器管理时,可以调用实体的set方法对数据进行修改,在容器决定flush时,更新的数据才会同步到数据库。如果希望修改后的数据实时同步到数据库,可以执行EntityManager.flush()方法。
publicboolean updateUser(Integer userid){
User user=(User)em.find(User.class,userid);
try{
user.setName("yyyyyy");
returntrue;
}catch(Exception e){
returnfalse;
}
}
59.5.4、合并merge()
merge()方法是在实体Bean已经脱离了EntityManager的管理时使用,当容器决定flush时,数据将会同步到数据库中。
publicboolean updateUser(User user){
try{
em.merge(user);
returntrue;
}catch(Exception e){
returnfalse;
}
}
执行em.merge(user)方法时,容器的工作规则:1)如果此时容器中已经存在一个受容器管理的具有相同ID的user实例,容器就会把参数user的内容拷贝进这个受管理的实例,merge()方法返回受管理的实例,但参数user仍然是分离的不受管理的。容器在决定Flush时把实例同步到数据库中;2)容器中不存在具有相同ID的user实例。容器根据传入的user参数拷贝出一个受容器管理的person实例,同时merge()会返回出这个受管理的实例,但是参数user仍然是分离的不受管理的。容器在决定Flush时把实例同步到数据库中。
如果传入merge()方法的参数不是实体Bean,会引发一个IllegalArgumentException例外。
59.5.5、删除remove()
publicboolean deleteUser(Integer id){
try{
User user=em.find(User.class, id);
em.remove(user);
returntrue;
}catch(Exception e){
returnfalse;
}
}
59.6、执行JPQL操作createQuery()
除了使用find()或getReference()方法来获得Entity Bean之外,还可以通过JPQL得到实体Bean。要执行JPQL语句,必须通过EntityManager的createQuery或createNamedQuery()方法创建一个Query对象。
public List queryList(){
Query query=em.createQuery("select u from User u where u.id=3");
return query.getResultList();
}
59.6.1、执行SQL操作createNativeQuery()
这里操作的是SQL语句,并非JPQL。
Query query=em.createQuery("select * from tbl_user u");
59.6.2、刷新实体refresh()
如果怀疑当前被管理的实体已经不是数据库中最新的数据,可以通过refresh()方法刷新实体,容器会把数据库中的新值重写进实体。这种情况一般发生在获取实体之后,有人更新了数据库中的记录,这是需要得到最新数据。当然再次调用find或getReference()方法也可以得到最新数据,但这种做法并不优雅。
em.refresh(user);
59.6.3、检测实体当前是否在被管理中contains()
contains()方法是用一个实体作为参数,如果这个实体对象当前正被持久化内容管理,返回为true,否则为false。
try{
User user=em.find(User.class, id);
if(em.contains(user)){
System.out.println("被管理中");
}else{
System.out.println("没有管理");
}
returntrue;
}catch(Exception e){
returnfalse;
}
59.6.4、分离所有当前正在被管理的实体clear()
在处理大量实体的时候,如果不把已经处理过的实体从EntityManager中分离出来,将会消耗大量的内存。调用EntityManager的clear()方法后,所有正在被管理的实体将会从持久化内容中分离出来。
有一点需要注意,在事务没有提交前,如果调用clear()方法之前对实体所作的任何改变将会丢失,所以建议在调用clear()方法之前先调用flush()方法保存更改。
59.6.5、将实体的改变立刻刷新到数据库中flush()
当实体管理器对象在一个Session Bean中使用时,它是和服务器的事务上下文绑定的。实体管理器在服务器的事务提交时并且同步内容。在一个Session Bean中,服务器的事务默认地会在调用堆栈的最后提交。为了只在当事务提交时才将改变更新到数据库中,容器将所有数据库操作集中到一个批量中,这样就减少了与数据库的交互。
当调用persist()、merge()或remove()这些方法时,更新并不会立刻同步到数据库中,直到容器决定刷新到数据库中时才会执行,默认情况下,容器决定刷新是在“相关查询”执行前或事务提交时发生,当然“相关查询”除find()和getReference()之外,这两个方法是不会引起容器触发刷新动作的,默认的刷新模式是可以改变的。
59.6.6、改变实体管理器的Flush模式setFlushMode()
默认情况下,实体管理器的Flush模式为AUTO。
两者的区别及使用场合:
FlushModeType.AUTO:刷新在查询语句执行前(除了find()和getReference())或事务提交时才发生,在大量更新数据的过程中没有任何查询语句的执行时使用。
FlushModeType.COMMIT:刷新只在事务提交时才发生,在大量更新数据的过程中存在查询的执行时使用。
59.6.7、获取持久化实现者的引用getDelegate()
可以获取EntityManager持久化实现者的引用。
@PersistenceContext(unitName="test")
private EntityManager em;
HibernateEntityManager hibernate=(HibernateEntityManager)em.getDelegate();
59.7、关系/对象映射
59.7.1、映射的表名或列名与数据库保留字同名时的处理
当映射的表名或列名于数据库保留字同名时,持久化引擎转译后的SQL在执行时将会出错。
如:
@Entity
@Table(name=”Order”)
public class Order implements Serializable{}
表名Order与排序保留字“Order”相同,导致SQL语出错。
针对上面的情况,在JBoss持久化产品中没有解决方案。一种变通的方法就是加上单引号或者方括号的解决方案。该方法针对具体数据库,不利于数据库移植。建议大家不要使用保留字作为表名或列名。
59.7.2、一对一映射
一对一关系,需要在关系维护端的@OneToOne注释中定义mappedBy属性。在关系被维护端建立外键列指向关系维护的主键列。
User代码:
@Entity
@Table(name="tbl_user")
publicclass User implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@Column(name="name")
private String name;
private String sex;
private String age;
@Temporal(value=TemporalType.DATE)
private Date birthday;
@OneToOne(optional=true,cascade=CascadeType.ALL,mappedBy="user")
private Card card;
public User() { }
public Card getCard() { returncard; }
publicvoid setCard(Card card) { this.card = card; }
public String getAge() { returnage; }
publicvoid setAge(String age) { this.age = age; }
public Date getBirthday() { returnbirthday; }
publicvoid setBirthday(Date birthday) {this.birthday = birthday; }
public String getName() { returnname; }
publicvoid setName(String name) { this.name = name; }
public String getSex() { returnsex; }
publicvoid setSex(String sex) { this.sex = sex; }
public Integer getId() { returnthis.id; }
publicvoid setId(Integer id) { this.id = id; }
publicint hashCode() { return (this.id== null) ? 0 : this.id.hashCode(); }
publicboolean equals(Object object) {
if (object instanceof User) {
final User obj = (User) object;
return (this.id != null) ?this.id.equals(obj.id) : (obj.id == null);
}
returnfalse;
}
}
@OneToOne注释指明User与Card为一对一关系,@OneToOne注释有5个属性:targetEntity、cascade、fetch、optional和mappedBy。
1)targetEntity:Class类型的属性
2)mappedBy:String类型的属性。定义类之间的双向关联。如果类之间是单向关系,不需要提供定义,如果类和类之间形成双向关系。就需要使用这个属性进行定义,否则可能引起数据一致性的问题。
3)cascade:CascadeType类型。该属性定义类和类之间的级联关系。定义级联关系将被容器视为当前类对象及其关联类对象采取相同的操作,而且这种关系是递归的。cascade的值只能从CascadeType.PERSIST(级联新建)、CascadeType.REMOVE(级联删除)、CascadeType.REFRESH(级联刷新)、Cascade.MERGE(级联更新)中选择一个或多个。还有一个选择是使用CascadeType.ALL,表示选择全部四项。
4)fetch:FetchType类型的属性。可选择项包括:FetchType.EAGER和FetchType.LAZY。前者表示关系类在主体类加载的时候同时加载,后者表示关系类在被访问时才加载。默认值是FetchType.LAZY。
5)optional:表示被维护对象是否需要存在。如果为真,说明card属性可以null,也就是允许没有身份证,未成年人就是没有身份证。
Card代码:
@Entity
@Table(name="tbl_card")
publicclass Card implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="id")
private Integer id;
@Column(name="cardno")
private String cardno;
@OneToOne(optional=false,cascade=CascadeType.REFRESH)
@JoinColumn(referencedColumnName="id")
private User user;
public Card() { }
public User getUser() { returnuser; }
publicvoid setUser(User user) { this.user = user; }
public String getCardno() { returncardno; }
publicvoid setCardno(String cardno) { this.cardno = cardno; }
public Integer getId() { returnthis.id; }
publicvoid setId(Integer id) { this.id = id; }
publicint hashCode() { return(this.id == null) ? 0 : this.id.hashCode(); }
publicboolean equals(Object object) {
if (object instanceof Card) {
final Card obj = (Card) object;
return (this.id != null) ?this.id.equals(obj.id) : (obj.id == null);
}
returnfalse;
}
}
@OneToOne注释指明Card与User为一对一关系,Card是关系被维护端,optional=false设置user属性值不能为空,也就是身份证必须有对应的主人。@JoinColumn(name=”user_id” referencedColumnName=”id” unique=”true”)指明tbl_card表的user_id列作为外键与tbl_user表的person_id列进行关联,unique=true指明user_id列的值不可重复。
59.7.3、一对多及多对一映射
@Entity
@Table(name="tbl_order")
publicclass Order implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="order_id")
private Integer id
private String name;
@OneToMany(targetEntity=OrderItem.class,cascade=CascadeType.ALL,mappedBy="order")
private Set set=new HashSet();
public Order() { }
public String getName() { returnname; }
publicvoid setName(String name) { this.name = name; }
public Set getSet() { returnset; }
publicvoid setSet(Set set) { this.set = set; }
public Integer getId() { returnthis.id; }
publicvoid setId(Integer id) { this.id= id; }
publicint hashCode() { return (this.id== null) ? 0 : this.id.hashCode(); }
publicboolean equals(Object object) {
if (object instanceof Order) {
final Order obj = (Order) object;
return (this.id != null) ?this.id.equals(obj.id) : (obj.id == null);
}
returnfalse;
}
}
--------------------------------------------------------------------------------------------------------
@Entity
@Table(name="tbl_item")
publicclass OrderItem implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="item_id")
private Integer id;
private String goodsname;
@ManyToOne(cascade=CascadeType.REFRESH,optional=false)
@JoinColumn(name="item_order_id",referencedColumnName="order_id")
private Order order;
public OrderItem() { }
public String getGoodsname() { returngoodsname; }
publicvoid setGoodsname(String goodsname) { this.goodsname = goodsname; }
public Order getOrder() { returnorder; }
publicvoid setOrder(Order order) { this.order = order; }
public Integer getId() { returnthis.id; }
publicvoid setId(Integer id) { this.id= id; }
publicint hashCode() { return (this.id == null) ? 0 : this.id.hashCode(); }
publicboolean equals(Object object) {
if (object instanceof OrderItem) {
final OrderItem obj = (OrderItem) object;
return (this.id != null) ?this.id.equals(obj.id) : (obj.id == null);
}
returnfalse;
}
}
59.7.4、多对多映射
@Entity
@Table(name="tbl_student")
publicclass Student implements Serializable{、
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="student_id")
private Integer id;
private String name;
@ManyToMany(cascade=CascadeType.ALL,targetEntity=Teacher.class)
@JoinTable(name="tbl_stu_teacher",inverseJoinColumns={
@JoinColumn(name="teacher_id",referencedColumnName="teacher_id")},
joinColumns={@JoinColumn(name="student_id",referencedColumnName="student_id")})
private Set set=new HashSet()
public Student() { }
public String getName() { returnname; }
publicvoid setName(String name) { this.name = name; }
public Set getSet() { returnset; }
publicvoid setSet(Set set) { this.set = set; }
public Integer getId() { returnthis.id; }
publicvoid setId(Integer id) { this.id= id; }
publicint hashCode() { return (this.id== null) ? 0 : this.id.hashCode(); }
publicboolean equals(Object object) {
if (object instanceof Student) {
final Student obj = (Student) object;
return (this.id != null) ?this.id.equals(obj.id) : (obj.id == null);
}
returnfalse;
}
}
-------------------------------------------------------------------------------------------------------------
@Entity
@Table(name="tbl_teacher")
publicclass Teacher implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="teacher_id")
private Integer id;
private String name;
@ManyToMany(targetEntity=Student.class,mappedBy="set")
private Set set=new HashSet();
public Teacher() { }
public String getName() { returnname; }
publicvoid setName(String name) { this.name = name; }
public Set getSet() { returnset; }
publicvoid setSet(Set set) { this.set = set; }
public Integer getId() { returnthis.id; }
publicvoid setId(Integer id) { this.id= id; }
publicint hashCode() { return (this.id== null) ? 0 : this.id.hashCode(); }
publicboolean equals(Object object) {
if (object instanceof Teacher) {
final Teacher obj = (Teacher) object;
return (this.id != null) ?this.id.equals(obj.id) : (obj.id == null);
}
returnfalse;
}
}
1.类级别注解
@Entity 映射实体类
@Table 映射数句库表
@Entity(name="tableName") - 必须,注解将一个类声明为一个实体bean。
属性:
name - 可选,对应数据库中的一个表。若表名与实体类名相同,则可以省略。
@Table(name="",catalog="",schema="") - 可选,通常和@Entity 配合使用,只能标注在实 体的 class 定义处,表示实体对应的数据库表的信息。
属性:
name - 可选,表示表的名称,默认地,表名和实体名称一致,只有在不一致的情况下才需 要指定表名
catalog - 可选,表示Catalog名称,默认为 Catalog("").
schema - 可选 , 表示 Schema 名称 , 默认为Schema("").
2.属性级别注解
@Id 映射生成主键
@Version 定义乐观锁
@Column 映射表的列
@Transient 定义暂态属性
2.1 与主键相关注解
@Id - 必须,定义了映射到数据库表的主键的属性,一个实体只能有一个属性被映射为主 键,置于 getXxxx() 前。
@GeneratedValue(strategy=GenerationType,generator="") - 可选,用于定义主键生成策略。
属性:
Strategy - 表示主键生成策略,取值有:
GenerationType.AUTO - 根据底层数据库自动选择(默认),若数据库支持自动 增 长类型,则为自动增长。
GenerationType.INDENTITY - 根据数据库的Identity字段生成,支持DB2、MySQL、 MS、SQL Server、SyBase与HyperanoicSQL数据库的Identity 类型主键。
GenerationType.SEQUENCE - 使用Sequence来决定主键的取值,适合Oracle、DB2等 支持Sequence的数据库,一般结合@SequenceGenerator使用。
(Oracle没有自动增长类型,只能用Sequence)
GenerationType.TABLE - 使用指定表来决定主键取值,结合@TableGenerator使用。
如:
@Id
@TableGenerator(name="tab_cat_gen",allocationSize=1)
@GeneratedValue(Strategy=GenerationType.Table)
Generator - 表示主键生成器的名称,这个属性通常和ORM框架相关 , 例如:
Hibernate 可以指定 uuid 等主键生成方式
@SequenceGenerator — 注解声明了一个数据库序列。
属性:
name - 表示该表主键生成策略名称,它被引用在@GeneratedValue中设置的“gernerator”值中。
sequenceName - 表示生成策略用到的数据库序列名称。
initialValue - 表示主键初始值,默认为0.
allocationSize - 每次主键值增加的大小,例如设置成1,则表示每次创建新记录后自动加1,默认为50.
示例 :
@Id
@GeneratedValues(strategy=StrategyType.SEQUENCE)
public int getPk() {
return pk;
}
Hibernate的访问类型为field时,在字段上进行注解声;
访问类型为property时,在getter方法上进行注释声明。
2.2 与非主键相关注解
@Version - 可以在实体bean中使用@Version注解,通过这种方式可添加对乐观锁定的支持
@Basic - 用于声明属性的存取策略:
@Basic(fetch=FetchType.EAGER) 即时获取(默认的存取策略)
@Basic(fetch=FetchType.LAZY) 延迟获取
@Temporal - 用于定义映射到数据库的时间精度:
@Temporal(TemporalType=DATE) 日期
@Temporal(TemporalType=TIME) 时间
@Temporal(TemporalType=TIMESTAMP) 两者兼具
@Column - 可将属性映射到列,使用该注解来覆盖默认值,@Column描述了数据库表中 该字段的详细定义,这对于根据 JPA 注解生成数据库表结构的工具非常有作用。
属性:
name - 可选,表示数据库表中该字段的名称,默认情形属性名称一致
nullable - 可选,表示该字段是否允许为 null,默认为 true
unique - 可选,表示该字段是否是唯一标识,默认为 false
length - 可选,表示该字段的大小,仅对 String 类型的字段有效,默认值255.
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;
}
@Transient - 可选,表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性,如果一个属性并非数据库表的字段映射,就务必将其标示为@Transient,否则ORM 框架默认其注解为 @Basic
示例 :
// 根据 birth 计算出 age 属性
@Transient
public int getAge() {
return getYear(new Date()) - getYear(birth);
}
2.3无注解属性的默认值
如果属性为单一类型,则映射为@Basic,
否则,如果属性对应的类型定义了@Embeddable注解,则映射为@Embedded,
否则,如果属性对应的类型实现了Serializable, 则属性被映射为@Basic并在一个列中保存该对象的serialized版本,
否则,如果该属性的类型为java.sql.Clob 或 java.sql.Blob,则作为@Lob并映射到适当的LobType.。
3.映射继承关系
@Inheritance注解来定义所选择的策略. 这个注解需要在每个类层次结构(class hierarchy) 最顶端的实体类上使用
4.映射实体bean的关联关系
4.1关联映射的一些定义
单向一对多:一方有集合属性,包含多个多方,而多方没有一方的引用。用户--->电子邮件
单向多对一:多方有一方的引用,一方没有多方的引用。论文类别---> 类别
双向一对多:两边都有多方的引用,方便查询。班级---> 学生
双向多对一:两边都有多方的引用,方便查询。
单向多对多:需要一个中间表来维护两个实体表。论坛--->文章
单向一对一:数据唯一,数据库数据也是一对一。舰船---> 水手
主键相同的一对一:使用同一个主键,省掉外键关联。客户---> 地址
单向:关系写哪边,就由谁管理。
双向:一般由多方管理。
@OneToMany(mappedBy="对方") //反向配置,对方管理。
4.2 关联映射的一些共有属性
@OneToOne、@OneToMany、@ManyToOne、ManyToMany的共有属性:
fetch - 配置加载方式。取值有
Fetch.EAGER - 及时加载,多对一默认是Fetch.EAGER
Fetch.LAZY - 延迟加载,一对多默认是Fetch.LAZY
cascade - 设置级联方式,取值有:
CascadeType.PERSIST - 保存
CascadeType.REMOVE - 删除
CascadeType.MERGE - 修改
CascadeType.REFRESH - 刷新
CascadeType.ALL - 全部
targetEntity - 配置集合属性类型,如:@OneToMany(targetEntity=Book.class)
@JoinColumn - 可选,用于描述一个关联的字段。
@JoinColumn和@Column类似,介量描述的不是一个简单字段,而是一个关联字段,例如描述一个 @ManyToOne 的字段。
属性:
name - 该字段的名称,由于@JoinColumn描述的是一个关联字段,如ManyToOne, 则默认的名称由其关联的实体决定。
例如,实体 Order 有一个user 属性来关联实体 User, 则 Order 的 user 属性为一个外键 ,
其默认的名称为实体User的名称 + 下划线 + 实体User的主键名称
4.3 一对一关联
@OneToOne – 表示一个一对一的映射
1.主表类A与从表类B的主键值相对应。
主表:@OneToOne(cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn
public B getB(){
Return b;
}
从表:无
2.主表A中有一个从表属性是B类型的b
主表:@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name="主表外键") //这里指定的是数据库中的外键字段。
public B getB(){
return b;
}
从表:无
3.主表A中有一个从表属性是B类型的b,同时,从表B中有一个主表属性是A类型的a
主表:@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name="主表外键") //这里指定的是数据库中的外键字段。
public B getB(){
return b;
}
从表:@OneToOne(mappedBy = "主表类中的从表属性")
public 主表类 get主表类(){
return 主表对象
}
注意:@JoinColumn是可选的。默认值是从表变量名+"_"+从表的主键(注意,这里加的是主键。而不是主键对应的变量)。
4.4 多对一关联
@ManyToOne - 表示一个多对一的映射,该注解标注的属性通常是数据库表的外键。
1.单向多对一:多方有一方的引用,一方没有多方的引用。
在多方
@ManyToOne(targetEntity=XXXX.class) //指定关联对象
@JoinColumn(name="") //指定产生的外键字段名
2.双向多对一:配置方式同双向一对多。
示例 :
// 订单 Order 和用户 User 是一个 ManyToOne 的关系
// 在 Order 类中定义
@ManyToOne()
@JoinColumn(name="USER")
public User getUser() {
return user;
}
4.5 一对多关联
@OneToMany - 描述一个一对多的关联,该属性应该为集合类型,在数据库中并没有实际字段。
1.单向一对多:一方有集合属性,包含多个多方,而多方没有一方的引用。
@OneToMany 默认会使用连接表做一对多关联
添加@JoinColumn(name="xxx_id") 后,就会使用外键关联,而不使用连接表了。
2.双向一对多
1)在多方
@ManyToOne
@JoinColumn(name="自己的数据库外键列名")
2)在一方
@OneToMany(mappedBy="多端的关联属性名")
@JoinColumn(name="对方的数据库外键列名")
4.6 多对多关联
@ManyToMany - 可选,描述一个多对多的关联。
属性:
targetEntity - 表示多对多关联的另一个实体类的全名,例如:package.Book.class
mappedBy - 用在双向关联中,把关系的维护权翻转。
1.单向多对多关联:
在主控方加入@ManyToMany注解即可。
2.双向多对多关联:
两个实体间互相关联的属性必须标记为@ManyToMany,并相互指定targetEntity属性。有且只有一个实体的@ManyToMany注解需要指定mappedBy属性,指向targetEntity的集合属性名称。