在EJB3.0中開發(fā)實(shí)體Bean非常簡單,你可以象開發(fā)一般的java bean一樣編程,只需做少量的注釋。一個(gè)實(shí)體bean不需要實(shí)現(xiàn)Home接口或者Remote、Local接口。
實(shí)體Bean通過EntityManager產(chǎn)生、查找、和持久層結(jié)合、從持久層收回等操作。
JBoss的EJB3.0架構(gòu)在Hibernate之上。
注釋:
@Entity:如果你要建立一個(gè)實(shí)體Bean的類,你必須在類上加上這個(gè)注釋,用來告訴容器這個(gè)類是實(shí)體Bean。這個(gè)Bean的主鍵由@Id指定。
這個(gè)注釋的聲明如下:
@Target(TYPE) @Retention(RUNTIME)
public @interface Entity {
String name() default "";
EntityType entityType() default CMP;
AccessType access() default PROPERTY;
int version() default 3;
} | name用來指定實(shí)體Bean的名稱,缺省和類名相同。
EntityType用來指定此bean是容器管理的持久實(shí)體Bean還是Bean管理的持久實(shí)體Bean。可以是CMP和BMP兩種方式。
AccessType用來指定容器訪問此EJB的持久化數(shù)據(jù)的方式。PROPERTY用來告訴容器使用get/set訪問持久化的數(shù)據(jù)(就是無Transient注釋的數(shù)據(jù)),F(xiàn)ILED告訴容器直接訪問字段,字段應(yīng)該聲明稱protected類型。
為了提供給其他會(huì)話Bean等客戶端使用,這個(gè)Bean應(yīng)實(shí)現(xiàn)Serializable接口。
實(shí)體Bean必須由一個(gè)無參數(shù)的構(gòu)造方法。
可持久化的屬性包括:java的基本類型(int,long等)、String、BigInteger、BigDecimal、java.util.Date、Calendar、java.sql.Date、java.sql.Time、java.sql.Timestamp、byte[]、char[]、其他實(shí)體Bean類型、其他實(shí)體Bean的集合(Collection、Set,不支持List)。
@Table
用來指定此實(shí)體Bean使用的主表,有時(shí)候可能需要其他的表,參看后面的章節(jié)的介紹。UniqueConstraint注釋用來添加約束條件。
@Id
用來指定此實(shí)體Bean的主鍵。它可以有多種生成方式:
·TABLE:容器指定用底層的數(shù)據(jù)表確保唯一。
·SEQUENCE:使用數(shù)據(jù)庫的SEQUENCE列來保證唯一
·IDENTITY:使用數(shù)據(jù)庫的INDENTIT列來保證唯一
·AUTO:由容器挑選一個(gè)合適的方式來保證唯一
·NONE:容器不負(fù)責(zé)主鍵的生成,由調(diào)用程序來完成。
@OnetoMany
兩個(gè)實(shí)體Bean之間可能有一對多、多對一、一對一、多對多的關(guān)系,后面兩個(gè)關(guān)系在后面的例子中介紹。
比如學(xué)生和各課分?jǐn)?shù)之間就是一對多的關(guān)系。
在EJB3.0中,一對多的關(guān)聯(lián)必須是雙向的,也就是說,必定有各多對一的關(guān)聯(lián)和它對應(yīng)。
OnetoMany注釋聲明如下:
@Target({METHOD, FIELD}) @Retention(RUNTIME)
public @interface OneToMany {
String targetEntity() default "";
CascadeType[] cascade() default {};
FetchType fetch() default LAZY;
} | 當(dāng)我們使用這個(gè)注釋為get方法注釋時(shí),如果使用JDK5.0的通用編程,返回集合Collection<目標(biāo)實(shí)體類型>,那么就不需要指定targetEntity的類型,否則返回類型聲明為普通的Collection的話,就必須聲明targetEntity的類型。
CascadeType指定了當(dāng)這個(gè)實(shí)體Bean新建或者M(jìn)erge的時(shí)候,與之關(guān)聯(lián)的實(shí)體需要怎樣的處理:
·MERGE:當(dāng)主實(shí)體Bean被merge的時(shí)候、關(guān)聯(lián)的實(shí)體Bean也被merge
·CREATE:當(dāng)主實(shí)體Bean被create的時(shí)候、關(guān)聯(lián)的實(shí)體Bean也被create
·REMOVE:當(dāng)主實(shí)體Bean被evict的時(shí)候、關(guān)聯(lián)的實(shí)體Bean也被evict
·ALL:包括以上的情況
FetchType指定從數(shù)據(jù)中讀取的方式:LAZY還是EAGER。LAZY只有當(dāng)?shù)谝淮卧L問的時(shí)候,才從數(shù)據(jù)庫中得到相關(guān)的實(shí)體bean,EAGER則很積極,同主實(shí)體Bean一同產(chǎn)生。
@ManytoOne
我們知道一對多的關(guān)聯(lián)是雙向的。在關(guān)聯(lián)的實(shí)體Bean中必定聲明了由ManyToOne注釋的方法。
@JoinColumn
我們知道兩個(gè)實(shí)體可以關(guān)聯(lián),但對應(yīng)到Table中需要指定一個(gè)列作為外鍵。假如不指定name,那么認(rèn)為主表中的列和附表中的主鍵有相同名稱的作為外鍵。如果不指定referencedColumnName,則認(rèn)為外鍵對應(yīng)副表的主鍵。
@JoinColumns
用來指示符合主鍵,在后面的章節(jié)中介紹。
這個(gè)例子主要有以下幾個(gè)文件,這個(gè)例子主要實(shí)現(xiàn)了管理學(xué)生分?jǐn)?shù)的功能。Student是一個(gè)實(shí)體Bean,管理學(xué)生的基本信息(姓名和各課分?jǐn)?shù)),其中學(xué)生的分?jǐn)?shù)又是一個(gè)實(shí)體Bean。TacherBean是一個(gè)無狀態(tài)的會(huì)話Bean,用來調(diào)用實(shí)體Bean。和前面的例子一樣,我們還是使用Client測試。
·Student.java:實(shí)體Bean。
·Score.java:實(shí)體Bean。
·Teacher.java:會(huì)話Bean的業(yè)務(wù)接口
·TeacherBean.java:會(huì)話Bean的實(shí)現(xiàn)類
·Client.java:測試EJB的客戶端類。
·jndi.properties:jndi屬性文件,提供訪問jdni的基本配置屬性。
·Build.xml:ant 配置文件,用以編譯、發(fā)布、測試、清除EJB。
下面針對每個(gè)文件的內(nèi)容做一個(gè)介紹。
Student.java
package com.kuaff.ejb3.entity;
import javax.ejb.CascadeType; import javax.ejb.Entity; import javax.ejb.FetchType; import javax.ejb.GeneratorType; import javax.ejb.Id; import javax.ejb.JoinColumn; import javax.ejb.OneToMany; import javax.ejb.Table; import java.util.ArrayList; import java.util.Collection; import java.io.Serializable;
@Entity @Table(name = "STUDENT")
public class Student implements Serializable { //主鍵 private int id; //學(xué)生名 private String name; //學(xué)生的分?jǐn)?shù) private Collection<Score> scores; //主鍵自動(dòng)產(chǎn)生 @Id(generate = GeneratorType.AUTO) 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 void addScores(String name,int number) { if (scores == null) { scores = new ArrayList<Score>(); } Score score = new Score(); score.setName(name); score.setNumber(number); score.setStudent(this); scores.add(score); }
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinColumn(name = "student_id")
public Collection<Score> getScores() { return scores; }
public void setScores(Collection<Score> scores) { this.scores = scores; } } | Student.java實(shí)現(xiàn)了Student實(shí)體Bean,它提供學(xué)生的基本情況以及學(xué)生的得分情況,得分是另外一個(gè)實(shí)體Bean。Student實(shí)體Bean和Score實(shí)體Bean是一對多的關(guān)系,站在Score的角度看是多對一的關(guān)系。
實(shí)體Bean需要使用@Entity做注釋,另外它指定這個(gè)實(shí)體Bean與表STUDENT對應(yīng)(通過注釋@Table(name = "STUDENT")),你可以在JBOSS的數(shù)據(jù)庫中看到這個(gè)表。
Score.java
package com.kuaff.ejb3.entity;
import java.io.Serializable; import javax.ejb.Entity; import javax.ejb.GeneratorType; import javax.ejb.Id; import javax.ejb.JoinColumn; import javax.ejb.ManyToOne; import javax.ejb.Table;
@Entity @Table(name = "Score")
public class Score implements Serializable { private int id; private String name; private int number; private Student student;
//主鍵自動(dòng)產(chǎn)生 @Id(generate = GeneratorType.AUTO)
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 getNumber() { return number; }
public void setNumber(int number) { this.number = number; }
@ManyToOne @JoinColumn(name = "student_id")
public Student getStudent() { return student; }
public void setStudent(Student student) { this.student = student; } } | 這個(gè)實(shí)體Bean存放學(xué)生的分?jǐn)?shù)。
Teacher.java
package com.kuaff.ejb3.entity;
import javax.ejb.Remote; import javax.ejb.Remove; import java.util.Map;
@Remote
public interface Teacher { public void addScore(String studentName,Map<String,Integer> map); public Student getStudent();
@Remove
public void leave(); } | 這個(gè)會(huì)話Bean接口提供增加分?jǐn)?shù)和得到用戶的方法。
TeacherBean.java
package com.kuaff.ejb3.entity;
import javax.ejb.EntityManager; import javax.ejb.Inject; import javax.ejb.Remove; import javax.ejb.Stateful; import java.util.Map; import java.util.Set;
@Stateful
public class TeacherBean implements Teacher { @Inject private EntityManager manager; private Student student;
public Student getStudent() { return student; }
public void addScore(String studentName, Map<String,Integer> map) { if (student == null) { student = new Student(); } student.setName(studentName); Set<String> set = map.keySet(); for (String sname:set) { student.addScores(sname,map.get(sname).intValue()); } }
@Remove public void leave() { manager.create(student); } } | 這個(gè)是會(huì)話Bean的實(shí)現(xiàn)類。
Client.java
package com.kuaff.ejb3.entity;
import java.util.Map; import java.util.HashMap; import java.util.Collection; import javax.naming.InitialContext; import javax.naming.NamingException;
public class Client { public static void main(String[] args) throws NamingException { InitialContext ctx = new InitialContext(); Teacher teacher = (Teacher) ctx.lookup(Teacher.class.getName()); Map<String,Integer> map = new HashMap<String,Integer>(); map.put("語文",new Integer(98)); map.put("化學(xué)",new Integer(149)); map.put("物理",new Integer(143)); teacher.addScore("smallnest",map); Student student = teacher.getStudent(); String name = student.getName(); System.out.printf("顯示%s的分?jǐn)?shù):%n",name); Collection<Score> c = student.getScores();
for (Score score:c) { System.out.printf("%s:%s%n",score.getName(),score.getNumber()+""); } } } | 這個(gè)客戶端增加學(xué)生的分?jǐn)?shù),并且測試顯示這個(gè)學(xué)生的相關(guān)信息。
請運(yùn)行{$JBOSS_HOME}/bin目錄下的run.bat: run –c all,啟動(dòng)JBOSS。
http://localhost:8080/jmx-console/HtmlAdaptor?action=inspectMBean&name=jboss%3Aservice%3DHypersonic%2Cdatabase%3DlocalDB,然后調(diào)用startDatabaseManager()方法,打開HSQL管理工具管理數(shù)據(jù)庫。
在Eclipse的Ant視圖中執(zhí)行ejbjar target。或者在命令行下,進(jìn)入到此工程目錄下,執(zhí)行ant ejbjar,將編譯打包發(fā)布此EJB。
在Eclipse的Ant視圖中執(zhí)行run target。或者在命令行下,進(jìn)入到此工程目錄下,執(zhí)行ant run,測試這個(gè)EJB。
| |
| |