Enterprise Java Development@TOPIC@
This chapter will take you through mapping nested classes to the database. In this case we have classes within classes (within classes, etc.) that get mapped to a flat database table. Note there are cases when the embedded class contains the meat of what we want mapped to our database and the wrapping entity may only be created to provide the necessary primary key property.
In this section we will map a simple embedded object within an entity class.
package myorg.entityex.annotated;
import javax.persistence.Column;
import javax.persistence.Embeddable;
@Embeddable
public class Name {
private String firstName;
private String lastName;
public String getFirstName() { return firstName; }
public Name setFirstName(String firstName) { this.firstName = firstName; return this; }
public String getLastName() { return lastName; }
public Name setLastName(String lastName) { this.lastName = lastName; return this; }
}
package myorg.entityex.annotated;
import javax.persistence.*;
@Entity
@Table(name="ENTITYEX_BEAR")
public class Bear {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
@Embedded
private Name name;
public int getId() { return id; }
public void setId(int id) {
this.id = id;
}
public Name getName() { return name; }
public void setName(Name name) {
this.name = name;
}
}
Add the new entity class to the persistence unit.
<class>myorg.entityex.annotated.Bear</class>
create table ENTITYEX_BEAR ( id integer generated by default as identity, firstName varchar(255), lastName varchar(255), primary key (id) );
public class Name {
@Column(name="FIRST_NAME", length=16)
private String firstName;
public class Bear {
...
@AttributeOverrides({
@AttributeOverride(name="lastName", column=@Column(name="LAST_NAME", length=16))
})
@Embedded
private Name name;
create table ENTITYEX_BEAR ( id integer generated by default as identity, FIRST_NAME varchar(16), LAST_NAME varchar(16), primary key (id) );
package myorg.entityex.annotated;
import javax.persistence.Embeddable;
@Embeddable
public class Street {
private int number;
private String name;
public int getNumber() { return number; }
public Street setNumber(int number) { this.number = number; return this; }
public String getName() { return name; }
public Street setName(String name) { this.name = name; return this; }
}
package myorg.entityex.annotated;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Embeddable;
@Embeddable
public class Address {
private Street street; //a second level of embedded
//@Column(name="CITY", length=16)
private String city;
//@Column(name="STATE", length=16)
private String state;
public Street getStreet() { return street; }
public Address setStreet(Street street) { this.street = street; return this; }
public String getCity() { return city; }
public Address setCity(String city) { this.city = city; return this; }
public String getState() { return state; }
public Address setState(String state) { this.state = state; return this; }
}
public class Bear { ... @Embedded private Address address; public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; }
create table ENTITYEX_BEAR ( id integer generated by default as identity, city varchar(255), state varchar(255), name varchar(255), number integer not null, FIRST_NAME varchar(16), LAST_NAME varchar(16), primary key (id) );
Define custom table mappings for the address.
public static class Street { private int number; private String name;
Map Street.number to the STREET_NUMBER column from the Address class.
@Embeddable public static class Address { @AttributeOverrides({ @AttributeOverride(name="number", column=@Column(name="STREET_NUMBER")), }) private Street street; //a second level of embedded
@AttributeOverrides({ @AttributeOverride(name="street.name", column=@Column(name="STREET_NAME", length=16)), }) @Embedded private Address address;
create table ENTITYEX_BEAR ( id integer generated by default as identity, CITY varchar(16), STATE varchar(16), STREET_NAME varchar(16), STREET_NUMBER integer, FIRST_NAME varchar(16), LAST_NAME varchar(16), primary key (id) );
Put the following test method within the existing JUnit test case.
@Test
public void testEmbeddedObject() {
log.info("testEmbeddedObject");
Bear bear = new Bear();
bear.setName(new Name().setFirstName("Yogi").setLastName("Bear"));
bear.setAddress(new Address()
.setCity("Jellystone Park")
.setState("???")
.setStreet(new Street().setNumber(1).setName("Picnic")));
em.persist(bear);
//flush to DB and get a new instance
em.flush(); em.detach(bear);
Bear bear2 = em.find(Bear.class, bear.getId());
assertEquals("unexpected firstName", bear.getName().getFirstName(), bear2.getName().getFirstName());
assertEquals("unexpected lastName", bear.getName().getLastName(), bear2.getName().getLastName());
assertEquals("unexpected street number",
bear.getAddress().getStreet().getNumber(), bear2.getAddress().getStreet().getNumber());
assertEquals("unexpected street name",
bear.getAddress().getStreet().getName(), bear2.getAddress().getStreet().getName());
assertEquals("unexpected city",
bear.getAddress().getCity(), bear2.getAddress().getCity());
assertEquals("unexpected state",
bear.getAddress().getState(), bear2.getAddress().getState());
}
Rebuild the module with the new test method in place.
-testEmbeddedObject Hibernate: insert into ENTITYEX_BEAR (id, CITY, STATE, STREET_NAME, STREET_NUMBER, FIRST_NAME, LAST_NAME) values (null, ?, ?, ?, ?, ?, ?) Hibernate: select bear0_.id as id7_0_, bear0_.CITY as CITY7_0_, bear0_.STATE as STATE7_0_, bear0_.STREET_NAME as STREET4_7_0_, bear0_.STREET_NUMBER as STREET5_7_0_, bear0_.FIRST_NAME as FIRST6_7_0_, bear0_.LAST_NAME as LAST7_7_0_ from ENTITYEX_BEAR bear0_ where bear0_.id=?