Hibernate @Entity注解配置说明
持久化是位于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节点用作指定持久化产品的各项属性。各个应用server使用的持久化产品都不一样。如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很easy。比起普通的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中使用时。它是和server的事务上下文绑定的。实体管理器在server的事务提交时而且同步内容。
在一个Session Bean中。server的事务默认地会在调用堆栈的最后提交。为了仅仅在当事务提交时才将改变更新到数据库中,容器将全部数据库操作集中到一个批量中,这样就降低了与数据库的交互。
当调用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;
}
}
posted on 2017-04-17 09:12 cynchanpin 阅读(363) 评论(0) 编辑 收藏 举报