Enterprise Java Development@TOPIC@
Now that we are done the tour into one-to-many relations and collections themselves, we can turn our attention on the many side and aspects associated with implementing a many-to-one relationship. Our first stab at this will stick to the uni-directional case. Many to one, uni-directional relationships are especially appropriate when the many side is huge and should best be obtained through a query rather than a through a collection in the parent entity. However, many things we learn here will apply to the one-to-many/many-to-one bi-directional case.
Create a JUnit test class to host tests for the many-to-one mappings.
package myorg.relex;
import static org.junit.Assert.*;
import javax.persistence.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.junit.*;
public class Many2OneTest extends JPATestBase {
private static Logger log = LoggerFactory.getLogger(Many2OneTest.class);
@Test
public void testSample() {
log.info("testSample");
}
}
Verify the new JUnit test class builds and executes to completion
relationEx]$ mvn clean test -P\!h2db -Ph2srv -Dtest=myorg.relex.Many2OneTest ... -HHH000401: using driver [org.h2.Driver] at URL [jdbc:h2:tcp://localhost:9092/./h2db/ejava] ... [INFO] BUILD SUCCESS
package myorg.relex.many2one;
import javax.persistence.*;
/**
* This class provides an example one/parent entity in a many-to-one, uni-directional relationship.
* For that reason -- this class will not have any reference to the many entities that may possibly
* reference it. These many/child objects must be obtained through the entity manager using a find or query.
*/
@Entity
@Table(name="RELATIONEX_STATE")
public class State {
@Id @Column(length=2)
private String id;
@Column(length=20, nullable=false)
private String name;
protected State() {}
public State(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() { return id; }
public String getName() { return name; }
public void setName(String name) {
this.name = name;
}
}
package myorg.relex.many2one;
import javax.persistence.*;
/**
* This class provides an example of the owning side of a many-to-one, uni-directional relationship
* that is realized through a foreign key from the child to the parent entity.
*/
@Entity
@Table(name="RELATIONEX_STATERES")
public class StateResident {
@Id @GeneratedValue
private int id;
@ManyToOne(
// optional=false,
// fetch=FetchType.EAGER
)
// @JoinColumn(
// name="STATE_ID",
// nullable=false
// )
private State state;
@Column(length=32)
private String name;
protected StateResident() {}
public StateResident(State state) {
this.state = state;
}
public int getId() { return id; }
public State getState() { return state; }
public void setState(State state) {
this.state = state;
}
public String getName() { return name; }
public void setName(String name) {
this.name = name;
}
}
Add the two entity classes to your persistence unit.
<class>myorg.relex.many2one.State</class>
<class>myorg.relex.many2one.StateResident</class>
$ mvn clean process-test-classes; more target/classes/ddl/relationEx-createJPA.ddl ... create table RELATIONEX_STATE ( id varchar(12) not null, name varchar(20) not null, primary key (id) ); create table RELATIONEX_STATERES ( id integer generated by default as identity, name varchar(32), state_id varchar(12), <!------- GENERATED FK Column primary key (id) ); ... alter table RELATIONEX_STATERES add constraint FK88A9D0FF4006DFB7 foreign key (state_id) references RELATIONEX_STATE;
@ManyToOne
@JoinColumn(
name="STATE_ID",
nullable=false
)
private State state;
$ mvn clean process-test-classes; more target/classes/ddl/relationEx-createJPA.ddl ... create table RELATIONEX_STATERES ( id integer generated by default as identity, name varchar(32), STATE_ID varchar(12) not null, primary key (id) ); ... alter table RELATIONEX_STATERES add constraint FK88A9D0FF4006DFB7 foreign key (STATE_ID) references RELATIONEX_STATE; ...
@ManyToOne(
optional=false)
@JoinColumn(
name="STATE_ID"//,
// nullable=false
)
private State state;
create table RELATIONEX_STATERES ( id integer generated by default as identity, name varchar(32), STATE_ID varchar(12) not null, primary key (id) );
@Test
public void testManyToOneUniFK() {
log.info("*** testManyToOneUniFK ***");
State state = new State("MD", "Maryland");
StateResident res = new StateResident(state);
res.setName("joe");
log.debug("persisting child");
em.persist(res);
log.debug("persisting parent");
em.persist(state);
em.flush();
}
$ mvn clean test -P\!h2db -Ph2srv -Dtest=myorg.relex.Many2OneTest#testManyToOneUniFK ... -*** testManyToOneUniFK *** -persisting child Hibernate: select state_.id, state_.name as name28_ from RELATIONEX_STATE state_ where state_.id=? Hibernate: insert into RELATIONEX_STATERES (id, name, STATE_ID) values (null, ?, ?) ... Tests in error: testManyToOneUniFK(myorg.relex.Many2OneTest): org.hibernate.exception.ConstraintViolationException: NULL not allowed for column "STATE_ID"; SQL statement:(..) ... [INFO] BUILD FAILURE
Re-order the creates to better simulate the parent being created prior to adding the child entities.
log.debug("persisting parent");
em.persist(state);
log.debug("persisting child");
em.persist(res);
em.flush();
$ mvn clean test -P\!h2db -Ph2srv -Dtest=myorg.relex.Many2OneTest#testManyToOneUniFK ... -persisting parent -persisting child Hibernate: insert into RELATIONEX_STATE (name, id) values (?, ?) Hibernate: insert into RELATIONEX_STATERES (id, name, STATE_ID) values (null, ?, ?) ... [INFO] BUILD SUCCESS
log.debug("getting new instances");
em.clear();
StateResident res2 = em.find(StateResident.class, res.getId());
log.debug("checking child");
assertEquals("unexpected child data", res.getName(), res2.getName());
log.debug("checking parent");
assertEquals("unexpected parent data", state.getName());
$ mvn clean test -P\!h2db -Ph2srv -Dtest=myorg.relex.Many2OneTest#testManyToOneUniFK ... -getting new instances Hibernate: select stateresid0_.id as id29_1_, stateresid0_.name as name29_1_, stateresid0_.STATE_ID as STATE3_29_1_, state1_.id as id28_0_, state1_.name as name28_0_ from RELATIONEX_STATERES stateresid0_ inner join <!=== EAGER fetch of parent RELATIONEX_STATE state1_ on stateresid0_.STATE_ID=state1_.id where stateresid0_.id=? -checking child -checking parent ... [INFO] BUILD SUCCESS
@ManyToOne( optional=false, fetch=FetchType.LAZY ) @JoinColumn(name="STATE_ID") private State state;
-getting new instances Hibernate: select stateresid0_.id as id29_0_, stateresid0_.name as name29_0_, stateresid0_.STATE_ID as STATE3_29_0_ from RELATIONEX_STATERES stateresid0_ where stateresid0_.id=? -checking child -checking parent Hibernate: select state0_.id as id28_0_, state0_.name as name28_0_ from RELATIONEX_STATE state0_ where state0_.id=? ... [INFO] BUILD SUCCESS
Add another child object for the same parent.
log.debug("add more residents"); StateResident resB = new StateResident(res2.getState()); em.persist(resB); em.flush();
$ mvn clean test -P\!h2db -Ph2srv -Dtest=myorg.relex.Many2OneTest#testManyToOneUniFK ... -add more residents Hibernate: insert into RELATIONEX_STATERES (id, name, STATE_ID) values (null, ?, ?) ... [INFO] BUILD SUCCESS
log.debug("getting new instances of residences"); em.clear(); List<StateResident> residents = em.createQuery( "select r from StateResident r " + "where r.state.id=:stateId", StateResident.class) .setParameter("stateId", res.getState().getId()) .getResultList(); assertEquals("unexpected number of residents", 2, residents.size());
$ mvn clean test -P\!h2db -Ph2srv -Dtest=myorg.relex.Many2OneTest#testManyToOneUniFK ... -getting new instances of residences Hibernate: select stateresid0_.id as id29_, stateresid0_.name as name29_, stateresid0_.STATE_ID as STATE3_29_ from RELATIONEX_STATERES stateresid0_ where stateresid0_.STATE_ID=? ... [INFO] BUILD SUCCESS
@ManyToOne( optional=false, fetch=FetchType.EAGER ) @JoinColumn(name="STATE_ID") private State state;
-getting new instances of residences Hibernate: select stateresid0_.id as id29_, stateresid0_.name as name29_, stateresid0_.STATE_ID as STATE3_29_ from RELATIONEX_STATERES stateresid0_ where stateresid0_.STATE_ID=? Hibernate: select state0_.id as id28_0_, state0_.name as name28_0_ from RELATIONEX_STATE state0_ where state0_.id=? ... [INFO] BUILD SUCCESS
log.debug("changing state/data of common parent");
residents.get(0).getState().setName("Home State");
assertEquals("unexpected difference in parent data",
residents.get(0).getState().getName(),
residents.get(1).getState().getName());
$ mvn clean test -P\!h2db -Ph2srv -Dtest=myorg.relex.Many2OneTest#testManyToOneUniFK ... -changing state/data of common parent Hibernate: update RELATIONEX_STATE set name=? where id=? ... [INFO] BUILD SUCCESS
In this example we will use an embeddable primary key class as an @EmbeddedId in the parent entity.
package myorg.relex.many2one;
import java.io.Serializable;
import javax.persistence.*;
/**
* This class provides an example compound primary key value that will be used in a many-to-one,
* uni-directional relationship.
*/
@Embeddable
public class HousePK implements Serializable {
private static final long serialVersionUID = 5213787609029123676L;
// @Column(name="NO")
private int number;
// @Column(name="STR", length=50)
private String street;
public HousePK() {}
public HousePK(int number, String street) {
this.number = number;
this.street = street;
}
public int getNumber() { return number; }
public void setNumber(int number) {
this.number = number;
}
public String getStreet() { return street; }
public void setStreet(String street) {
this.street = street;
}
@Override
public int hashCode() {
return number + street==null?0:street.hashCode();
}
@Override
public boolean equals(Object obj) {
try {
if (this==obj) { return true; }
HousePK rhs = (HousePK)obj;
if (street==null && rhs.street != null) { return false; }
return number==rhs.number && street.equals(rhs.street);
} catch (Exception ex) { return false; }
}
}
package myorg.relex.many2one;
import javax.persistence.*;
/**
* This class provides an example of a parent/inverse side of a many-to-one, uni-directional relationship where
* the parent and foreign key must use a compound value.
*/
@Entity
@Table(name="RELATIONEX_HOUSE")
public class House {
@EmbeddedId
// @AttributeOverrides({
// @AttributeOverride(name="street", column=@Column(name="STREET_NAME", length=20)),
// })
private HousePK id;
@Column(length=16, nullable=false)
private String name;
protected House() {}
public House(HousePK id, String name) {
this.id = id;
this.name = name;
}
public HousePK getId() { return id; }
public String getName() { return name; }
public void setName(String name) {
this.name = name;
}
}
package myorg.relex.many2one;
import javax.persistence.*;
/**
* This class provides an example of the owning/child side of a many-to-one, uni-directional relationship
* where the parent uses a (embedded) compound primary key.
*/
@Entity
@Table(name="RELATIONEX_OCCUPANT")
public class Occupant {
@Id @GeneratedValue
private int id;
@ManyToOne(optional=false)
// @JoinColumns({
// @JoinColumn(name="RES_NUM", referencedColumnName="NUMBER"),
// @JoinColumn(name="RES_STR", referencedColumnName="STREET_NAME")
// })
private House residence;
@Column(length=16, nullable=false)
private String name;
protected Occupant(){}
public Occupant(String name, House residence) {
this.name = name;
this.residence = residence;
}
public int getId() { return id; }
public House getResidence() { return residence; }
public void setResidence(House residence) {
this.residence = residence;
}
public String getName() { return name; }
public void setName(String name) {
this.name = name;
}
}
Add the new entity classes to the persistence unit. Do *not* list the primary key class.
<class>myorg.relex.many2one.House</class>
<class>myorg.relex.many2one.Occupant</class>
$ mvn clean process-test-classes; more target/classes/ddl/relationEx-createJPA.ddl ... create table RELATIONEX_HOUSE ( number integer not null, street varchar(255) not null, name varchar(16) not null, primary key (number, street) ); ... create table RELATIONEX_OCCUPANT ( id integer generated by default as identity, name varchar(16) not null, residence_number integer not null, residence_street varchar(255) not null, primary key (id) ); ... alter table RELATIONEX_OCCUPANT add constraint FK6957B84D35A694BB foreign key (residence_number, residence_street) references RELATIONEX_HOUSE;
@Column(name="NO")
private int number;
@Column(name="STR", length=50)
private String street;
$ mvn clean process-test-classes; more target/classes/ddl/relationEx-createJPA.ddl ... create table RELATIONEX_HOUSE ( NO integer not null, STR varchar(50) not null, name varchar(16) not null, primary key (NO, STR) ); ... create table RELATIONEX_OCCUPANT ( id integer generated by default as identity, name varchar(16) not null, residence_NO integer not null, residence_STR varchar(50) not null, primary key (id) ); ... alter table RELATIONEX_OCCUPANT add constraint FK6957B84D81D611CF foreign key (residence_NO, residence_STR) references RELATIONEX_HOUSE;
public class House {
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name="street", column=@Column(name="STREET_NAME", length=20)),
})
private HousePK id;
create table RELATIONEX_HOUSE ( NO integer not null, STREET_NAME varchar(20) not null, name varchar(16) not null, primary key (NO, STREET_NAME) ); ... create table RELATIONEX_OCCUPANT ( id integer generated by default as identity, name varchar(16) not null, residence_NO integer not null, residence_STREET_NAME varchar(20) not null, primary key (id) ); ... alter table RELATIONEX_OCCUPANT add constraint FK6957B84D8AD189E5 foreign key (residence_NO, residence_STREET_NAME)
public class Occupant {
...
@ManyToOne(optional=false)
@JoinColumns({
@JoinColumn(name="RES_NUM", referencedColumnName="NO"),
@JoinColumn(name="RES_STR", referencedColumnName="STREET_NAME")
})
private House residence;
create table RELATIONEX_HOUSE ( NO integer not null, STREET_NAME varchar(20) not null, name varchar(16) not null, primary key (NO, STREET_NAME) ); ... create table RELATIONEX_OCCUPANT ( id integer generated by default as identity, name varchar(16) not null, RES_NUM integer not null, RES_STR varchar(20) not null, primary key (id) ); ... alter table RELATIONEX_OCCUPANT add constraint FK6957B84D739A6436 foreign key (RES_NUM, RES_STR) references RELATIONEX_HOUSE;
@Test
public void testManyToOneUniCompoundFK() {
log.info("*** testManyToOneUniCompoundFK ***");
House house = new House(new HousePK(1600,"PA Ave"),"White House");
Occupant occupant = new Occupant("bo", house);
log.debug("persisting parent");
em.persist(house);
log.debug("persisting child");
em.persist(occupant);
em.flush();
$ mvn clean test -P\!h2db -Ph2srv -Dtest=myorg.relex.Many2OneTest#testManyToOneUniCompoundFK ... -*** testManyToOneUniCompoundFK *** -persisting parent -persisting child Hibernate: insert into RELATIONEX_HOUSE (name, NO, STREET_NAME) values (?, ?, ?) Hibernate: insert into RELATIONEX_OCCUPANT (id, name, RES_NUM, RES_STR) values (null, ?, ?, ?) ... [INFO] BUILD SUCCESS
log.debug("getting new instances");
em.clear();
Occupant occupant2 = em.find(Occupant.class, occupant.getId());
log.debug("checking child");
assertEquals("unexpected child data", occupant.getName(), occupant2.getName());
log.debug("checking parent");
assertEquals("unexpected parent data", house.getName(), occupant2.getResidence().getName());
$ mvn clean test -P\!h2db -Ph2srv -Dtest=myorg.relex.Many2OneTest#testManyToOneUniCompoundFK ... -getting new instances Hibernate: select occupant0_.id as id31_1_, occupant0_.name as name31_1_, occupant0_.RES_NUM as RES3_31_1_, occupant0_.RES_STR as RES4_31_1_, house1_.NO as NO30_0_, house1_.STREET_NAME as STREET2_30_0_, house1_.name as name30_0_ from RELATIONEX_OCCUPANT occupant0_ inner join RELATIONEX_HOUSE house1_ on occupant0_.RES_NUM=house1_.NO and occupant0_.RES_STR=house1_.STREET_NAME where occupant0_.id=? -checking child -checking parent ... [INFO] BUILD SUCCESS
log.debug("add more child entities");
Occupant occupantB = new Occupant("miss beazily", occupant2.getResidence());
em.persist(occupantB);
em.flush();
$ mvn clean test -P\!h2db -Ph2srv -Dtest=myorg.relex.Many2OneTest#testManyToOneUniCompoundFK ... -add more child entities Hibernate: insert into RELATIONEX_OCCUPANT (id, name, RES_NUM, RES_STR) values (null, ?, ?, ?) ... [INFO] BUILD SUCCESS
log.debug("getting new instances of children");
em.clear();
List<Occupant> occupants = em.createQuery(
"select o from Occupant o " +
"where o.residence.id=:houseId",
Occupant.class)
.setParameter("houseId", occupant.getResidence().getId())
.getResultList();
assertEquals("unexpected number of children", 2, occupants.size());
-getting new instances of children Hibernate: select occupant0_.id as id31_, occupant0_.name as name31_, occupant0_.RES_NUM as RES3_31_, occupant0_.RES_STR as RES4_31_ from RELATIONEX_OCCUPANT occupant0_ where occupant0_.RES_NUM=? <!== provider created breakdown from PK comparison and occupant0_.RES_STR=? to individual column comparisons Hibernate: select house0_.NO as NO30_0_, house0_.STREET_NAME as STREET2_30_0_, house0_.name as name30_0_ from RELATIONEX_HOUSE house0_ where house0_.NO=? and house0_.STREET_NAME=? ... [INFO] BUILD SUCCESS
package myorg.relex.many2one;
import javax.persistence.*;
/**
* This class is an example of a parent in a many-to-one, uni-directional relation where the
* primary key of the child is derived from the primary key of the parent.
*/
@Entity
@Table(name="RELATIONEX_ITEMTYPE")
public class ItemType {
@Id @GeneratedValue
private int id;
@Column(length=20, nullable=false)
private String name;
protected ItemType() {}
public ItemType(String name) {
this.name = name;
}
public int getId() { return id; }
public String getName() { return name; }
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return id +":" + name;
}
}
package myorg.relex.many2one;
import java.io.Serializable;
import javax.persistence.*;
/**
* This class provides an example primary key class for a child entity that
* derives one of its primary key values from its parent entity in a many-to-one
* relationship.
*/
@SuppressWarnings("serial")
@Embeddable
public class ItemPK implements Serializable {
public class ItemPK implements Serializable {
// @Column(name="TYPE_ID_PK")
private int typeId; //unique value from parent ItemType.id
// @Column(name="NUMBER_PK")
private int number; //unique value assigned to instances of Item
public int getTypeId() { return typeId; }
public ItemPK setTypeId(int typeId) {
this.typeId = typeId;
return this;
}
public int getNumber() { return number; }
public ItemPK setNumber(int number) {
this.number = number;
return this;
}
@Override
public int hashCode() {
return typeId + number;
}
@Override
public boolean equals(Object obj) {
try {
if (this == obj) { return true; }
ItemPK rhs = (ItemPK) obj;
return typeId==rhs.typeId && number==rhs.number;
} catch (Exception ex) { return false; }
}
@Override
public String toString() {
return "(typeId=" + typeId + ",number=" + number + ")";
}
}
package myorg.relex.many2one;
import java.util.Date;
import javax.persistence.*;
/**
* This class provides an example of a child entity that derives its primary key from
* the parent/one side of a many-to-one relation.
*/
@Entity
@Table(name="RELATIONEX_ITEM")
public class Item {
@EmbeddedId
private ItemPK id;
@ManyToOne(optional=false)
// @MapsId("typeId") //refers to the ItemPK.typeId property
// @JoinColumn(name="TYPE_ID")
private ItemType itemType;
@Temporal(TemporalType.TIMESTAMP)
private Date created;
protected Item() {}
public Item(ItemType itemType, int number) {
this.itemType = itemType;
//typeId in PK auto-mapped to itemType FK
this.id = new ItemPK().setNumber(number);
}
public ItemPK getId() { return id; }
public ItemType getItemType() { return itemType; }
public Date getCreated() { return created; }
public void setCreated(Date created) {
this.created = created;
}
@Override
public String toString() {
return (itemType==null?null:itemType) + "pk=" + id;
}
}
Add the two entity classes to your peristence unit. Do *not* list the primary key class.
<class>myorg.relex.many2one.Item</class>
<class>myorg.relex.many2one.ItemType</class>
$ mvn clean process-test-classes; more target/classes/ddl/relationEx-createJPA.ddl ... create table RELATIONEX_ITEM ( number integer not null, typeId integer not null, created timestamp, itemType_id integer not null, primary key (number, typeId) ); create table RELATIONEX_ITEMTYPE ( id integer generated by default as identity, name varchar(20) not null, primary key (id) ); ... alter table RELATIONEX_ITEM add constraint FK355BBDA3C6C591FD foreign key (itemType_id) references RELATIONEX_ITEMTYPE;
@ManyToOne(optional=false)
@MapsId("typeId") //refers to the ItemPK.typeId property
private ItemType itemType;
$ mvn clean process-test-classes; more target/classes/ddl/relationEx-createJPA.ddl ... create table RELATIONEX_ITEM ( number integer not null, created timestamp, itemType_id integer, primary key (number, itemType_id) ); create table RELATIONEX_ITEMTYPE ( id integer generated by default as identity, name varchar(20) not null, primary key (id) ); ... alter table RELATIONEX_ITEM add constraint FK355BBDA3C6C591FD foreign key (itemType_id) references RELATIONEX_ITEMTYPE;
Make a small cosmetic change by defining the column name for the foreign key column.
@ManyToOne(optional=false)
@MapsId("typeId") //refers to the ItemPK.typeId property
@JoinColumn(name="TYPE_ID")
private ItemType itemType;
Notice we are renaming the foreign key that the primary key is mapped to and not the primary key.
$ mvn clean process-test-classes; more target/classes/ddl/relationEx-createJPA.ddl ... create table RELATIONEX_ITEM ( number integer not null, TYPE_ID integer, created timestamp, primary key (number, TYPE_ID) ); create table RELATIONEX_ITEMTYPE ( id integer generated by default as identity, name varchar(20) not null, primary key (id) ); ... alter table RELATIONEX_ITEM add constraint FK355BBDA349D11870 foreign key (TYPE_ID) references RELATIONEX_ITEMTYPE;
public class ItemPK implements Serializable {
@Column(name="TYPE_ID_PK")
private int typeId; //unique value from parent ItemType.id
@Column(name="NUMBER_PK")
private int number; //unique value assigned to instances of Item
$ mvn clean process-test-classes; more target/classes/ddl/relationEx-createJPA.ddl ... create table RELATIONEX_ITEM ( NUMBER_PK integer not null, TYPE_ID integer, created timestamp, primary key (NUMBER_PK, TYPE_ID) ); create table RELATIONEX_ITEMTYPE ( id integer generated by default as identity, name varchar(20) not null, primary key (id) ); ... alter table RELATIONEX_ITEM add constraint FK355BBDA349D11870 foreign key (TYPE_ID) references RELATIONEX_ITEMTYPE;
@Test
public void testManyToOneUniMapsIdEmbedded() {
log.info("*** testManyToOneUniMapsIdEmbedded ***");
ItemType type = new ItemType("snowblower");
log.debug("persisting parent:" + type);
em.persist(type);
em.flush();
log.debug("persisted parent:" + type);
Item item = new Item(type,1);
item.setCreated(new Date());
log.debug("persisting child:" + item);
em.persist(item);
em.flush();
log.debug("persisted child:" + item);
//check PK assigned
ItemPK pk = new ItemPK().setTypeId(type.getId()).setNumber(1);
assertTrue(String.format("expected PK %s not match actual %s", pk,
item.getId()), pk.equals(item.getId()));
}
$ mvn clean test -P\!h2db -Ph2srv -Dtest=myorg.relex.Many2OneTest#testManyToOneUniMapsIdEmbedded ... -persisting parent:0:snowblower Hibernate: insert into RELATIONEX_ITEMTYPE (id, name) values (null, ?) -persisted parent:1:snowblower -persisting child:1:snowblowerpk=(typeId=0,number=1) Hibernate: ^ insert +- unassigned into RELATIONEX_ITEM (created, NUMBER_PK, TYPE_ID) values +- assigned by provider (?, ?, ?) v -persisted child:1:snowblowerpk=(typeId=1,number=1) ... [INFO] BUILD SUCCESS
log.debug("getting new instances");
em.clear();
Item item2 = em.find(Item.class, pk);
log.debug("checking child");
assertNotNull("child not found by primary key:" + pk, item2);
assertTrue("unexpected child data", item.getCreated().equals(item2.getCreated()));
log.debug("checking parent");
assertEquals("unexpected parent data", type.getName(), item2.getItemType().getName());
$ mvn clean test -P\!h2db -Ph2srv -Dtest=myorg.relex.Many2OneTest#testManyToOneUniMapsIdEmbedded ... -getting new instances Hibernate: select item0_.NUMBER_PK as NUMBER1_32_1_, item0_.TYPE_ID as TYPE2_32_1_, item0_.created as created32_1_, itemtype1_.id as id33_0_, itemtype1_.name as name33_0_ from RELATIONEX_ITEM item0_ inner join RELATIONEX_ITEMTYPE itemtype1_ on item0_.TYPE_ID=itemtype1_.id where item0_.NUMBER_PK=? and item0_.TYPE_ID=? -checking child -checking parent ... [INFO] BUILD SUCCESS
Item itemB = new Item(item2.getItemType(),2);
log.debug("add more child entities:" + itemB);
itemB.setCreated(new Date());
em.persist(itemB);
em.flush();
log.debug("new child entities added:" + itemB);
$ mvn clean test -P\!h2db -Ph2srv -Dtest=myorg.relex.Many2OneTest#testManyToOneUniMapsIdEmbedded ... -add more child entities:1:snowblowerpk=(typeId=0,number=2) Hibernate: ^ insert +- unassigned into RELATIONEX_ITEM (created, NUMBER_PK, TYPE_ID) values +- assigned by provider (?, ?, ?) v -new child entities added:1:snowblowerpk=(typeId=1,number=2) ... [INFO] BUILD SUCCESS
log.debug("getting new instances of children");
em.clear();
List<Item> items = em.createQuery(
"select i from Item i " +
"where i.itemType.id=:typeId",
Item.class)
.setParameter("typeId", item.getItemType().getId())
.getResultList();
assertEquals("unexpected number of children", 2, items.size());
-getting new instances of children Hibernate: select item0_.NUMBER_PK as NUMBER1_32_, item0_.TYPE_ID as TYPE2_32_, item0_.created as created32_ from RELATIONEX_ITEM item0_ where item0_.TYPE_ID=? Hibernate: select itemtype0_.id as id33_0_, itemtype0_.name as name33_0_ from RELATIONEX_ITEMTYPE itemtype0_ where itemtype0_.id=? ... [INFO] BUILD SUCCESS
List<Item> items = em.createQuery(
"select i from Item i " +
//"where i.itemType.id=:typeId",
"where i.id.typeId=:typeId",
Item.class)
Hibernate: select item0_.NUMBER_PK as NUMBER1_32_, item0_.TYPE_ID as TYPE2_32_, item0_.created as created32_ from RELATIONEX_ITEM item0_ where item0_.TYPE_ID=?
This example used the @EmbeddedId. Another option would have been the use of an @IdClass.