- EJB 3 Developer Guide
- Michael Sikora
- 1399字
- 2021-07-02 11:34:56
O/R Mapping Additional Annotations
We will modify our application again and introduce some more object-relational mapping annotations. Specifically we will cover the @Embedded, @Embeddable, @Enumerated
, and @MapKey
annotations.
First we decide to embed the Referee entity within the Customer entity. An embedded Referee entity shares the identity of the owning Customer entity. A Referee entity cannot exist on its own without a corresponding Customer entity. Such a Referee entity cannot be independently persisted, it is persisted by default whenever the owning Customer entity is persisted. However, we still map a Referee object onto a relational database in the usual manner.
We also decide to add an enumerated type, gender
, as a Customer attribute.
To indicate an entity is embedded EJB 3 provides two annotations: @Embedded
and @Embeddable. @Embedded
is used within the owning entity and @Embeddable
within the embedded entity. Below is the modified version of the Customer entity:
@Entity public class Customer implements Serializable { private int id; private String firstName; private String lastName; private Referee referee; private Collection<Address> addresses; private Map<String, Account> accounts = new HashMap<String, Account>(); public Customer() {} @Id @Column(name="CUSTOMER_ID") public int getId() { return id; } public void setId(int id) { this.id = id; } @Column(name="FIRST_NAME", length=30) public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } @Column(name="LAST_NAME", length=30) public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @OneToOne @Embedded public Referee getReferee() { return referee; } public void setReferee(Referee referee) { this.referee = referee; } @OneToMany(mappedBy="customer", fetch=EAGER) @MapKey() public Map<String, Account> getAccounts() { return accounts; } public void setAccounts(Map<String, Account> accounts) { this.accounts = accounts; } @ManyToMany(fetch=EAGER) @JoinTable( name="CUSTOMER_ADDRESS", joinColumns=@JoinColumn( name="CUST_ID", referencedColumnName="CUSTOMER_ID"), inverseJoinColumns=@JoinColumn( name="ADD_ID", referencedColumnName="ADDRESS_ID") ) public Collection<Address> getAddresses() { return addresses; } public void setAddresses(Collection<Address> addresses) { this.addresses = addresses; } private Gender gender; @Column(name="GENDER", length=20) @Enumerated(STRING) public Gender getGender() { return gender; } public void setGender(Gender gender) { this.gender = gender; } public String toString() { return "[Customer Id =" + id + ",first name=" + firstName + ",last name=" + lastName + ", referee=" + referee + ",addresses=" + addresses + ",accounts=" + accounts + ",gender=" + gender + "]"; } }
As you can see in the section above, we have added the @Embedded
annotation on the getRefeee()
method to indicate that the Referee entity is embedded.
Note that we have added a new field, gender
, to the Customer entity. The gender
field will be persisted as an enumerated type with enumeration constants of MALE
and FEMALE
. This is implemented in EJB 3 using the @Enumerated
annotation. This can be a field-based or property-based annotation. In our example we prefix the getGender()
method with @Enumerated
:
private Gender gender; @Column(name="GENDER", length=20) @Enumerated(STRING) public Gender getGender() { return gender; } public void setGender(Gender gender) { this.gender = gender; }
Note that we have specified STRING
in the @Enumerated
annotation. This specifies that the field will be mapped as a string. If we specify ORDINAL
, which is the default, then the field will be mapped as an integer.
We now need to create the enumeration class itself, Gender.java
:
package ejb30.entity; public enum Gender { MALE, FEMALE }
Because we specified a value of STRING
in the @Enumerated
annotation, one of the string values MALE
or FEMALE
will be stored in the database. If we had specified ORDINAL
, then 0 would be stored in the database if the enumeration value is MALE
and 1 would be stored if the enumeration value is FEMALE
.
We will make one more modification to the Customer entity. We want to store Account objects associated with a Customer as a Map. We define an accounts object to be a Map
in the usual manner:
private Map<String, Account> accounts = new HashMap<String, Account>();
This defines a Map, accounts
, where the key
is a String
type and the value
is an Account object.
We now need to use the @MapKey
annotation and modify the associated getter and setter as follows:
@OneToMany(mappedBy="customer", fetch=EAGER) @MapKey() public Map<String, Account> getAccounts() {return accounts;} public void setAccounts(Map<String, Account> accounts) { this.accounts = accounts; }
The @MapKey
defaults to storing the primary key of the associated entity as the map key. In this case the id
property of Account is stored. If we want to specify a property other than the primary key we need to use the name
attribute of @MapKey
. For example, if in our banking example we also had a rollNumber
property which is unique for an Account object, we would specify:
@MapKey(name=rollNumber)
Note that we also modified the return type of getAccounts()
and the argument type of setAccounts().
Referee Class
We now turn to the Referee class:
//@Entity
@Embeddable
public class Referee implements Serializable {
private int id;
private String name;
private String comments;
// getters and setters
}
We have added the @Embeddable
annotation to indicate that all Referee fields are persisted only when the Customer entity is persisted. Because Referee cannot be persisted on its own it is no longer an entity so we do not use the @Entity
annotation. There are no other changes to the Referee class. We cannot use any mapping annotations apart from @Basic, @Column, @Lob, @Temporal
, and @Enumerated
within an embeddable class.
BankServiceBean
We need to make a few changes to BankServiceBean
as shown in the following code fragment:
@Stateless public class BankServiceBean implements BankService { @PersistenceContext(unitName="BankService") private EntityManager em; public void createCustomers() { Referee r1 = new Referee(); r1.setId(1); r1.setName("SIR JOHN DEED"); r1.setComments("JUDGE"); // em.persist(r1); Customer c1 = new Customer(); c1.setId(1); c1.setFirstName("SIMON"); c1.setLastName("KING"); c1.setReferee(r1); c1.setGender(Gender.MALE); Account a1 = new Account(); a1.setId(1); a1.setBalance(430.5); a1.setAccountType("C"); a1.setCustomer(c1); Map<String, Account> accounts1 = new HashMap<String, Account>(); accounts1.put("1", a1); c1.setAccounts(accounts1); em.persist(a1); ... } public Map<String, Account> findAccountForCustomer( int custId) { Customer customer = (Customer) em.find(Customer.class, custId); Map<String, Account> accounts = customer.getAccounts(); return accounts; } // other finder methods }
First, as Referee is now an embedded entity, statements which persist instances of the Referee entity need to be deleted:
// em.persist(r1);
Next, as each Customer object now has an associated Gender
enumerated type, we need to invoke the setGender()
method passing either a MALE
or FEMALE
argument:
c1.setGender(Gender.MALE);
Finally, we have added the findAccountForCustomer()
method which returns a Map
of accounts for a given customer.
Composite Primary Keys
It is good practice to have a single column as a primary key. It is also good practice for the primary key value to be obtained from a sequence and not contain any data. A primary key containing data is very hard to update. A primary key obtained from a sequence is known as a surrogate key. The main advantage of a surrogate key is that we never need to update the key. However, there may be occasions where we need to map an entity to a legacy relational database which has a composite primary key for the corresponding table.
Suppose our Customer entity has a composite primary key of FIRST_NAME, LAST_NAME
in the corresponding CUSTOMER
table. First we must define a primary key class, CustomerPK.java
, as follows:
public class CustomerPK implements Serializable { private String firstName = ""; private String lastName = ""; public CustomerPK() {} public CustomerPK(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } // getter and setter methods. // equals and hashcode method. }
A primary key class must be public, serializable, and have a public no-arg constructor. The class must also define equals
and hashcode
methods.
Now let's look at the Customer entity:
@Entity @IdClass(CustomerPK.class) public class Customer implements Serializable { ... @Id @Column(name="FIRST_NAME", length=30) public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } @Id @Column(name="LAST_NAME", length=30) public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } ... }
Note that we use the class level @IdClass
annotation to specify the primary key class associated with the Customer entity. We use the property-based @Id
annotation for each of the getter methods corresponding to the fields, firstName
and lastName
, making up the primary key.
Now look at the Account entity:
@Entity
public class Account implements Serializable {
...
@ManyToOne
@JoinColumns({
@JoinColumn(name="CUSTOMER_FNAME",
referencedColumnName="FIRST_NAME"),
@JoinColumn(name="CUSTOMER_LNAME",
referencedColumnName="LAST_NAME")
})
public Customer getCustomer() {
return customer;
}
...
}
When defining the many-to-one relationship between Account and Customer, we need to modify the join column definitions. We now have more than one join column; actually two columns which comprise the composite key. Consequently we use the @JoinColumns
annotation wrapped around the individual @JoinColumn
annotations.
- 3ds Max 2016中文版完全自學手冊
- 從零開始:Photoshop CC中文版基礎培訓教程
- 創(chuàng)意UI:Photoshop玩轉移動UI設計
- IBM Lotus Notes 8.5 User Guide
- 中文版3ds Max 2016/VRay效果圖制作實戰(zhàn)基礎教程
- Microsoft BizTalk Server 2010 Patterns
- jQuery Mobile First Look
- Instant Testing with QUnit
- Indesign平面排版技術應用
- 中文版Flash CS6動畫制作(慕課版)
- 剪映+Vlog+Premiere短視頻制作從新手到高手
- CorelDRAW X6平面設計與制作案例教程
- 24小時玩賺剪映
- 中文版CorelDRAW X7技術大全
- AutoCAD 2010 建筑設計與制作技能基礎教程