I was trying to create a relationship without using surrogate primary keys the other day in Hibernate. I started searching around for examples, but did not find many examples that used Annotations.
So here is what I implemented:
I have an Application that can have many Accounts spread across many Machines. Each Machine can only have 1 Account name per Application on a given Machine. Thus, there will be many Accounts, but only 1 Account per Application.
So I first create an AccountPK as my composite Primary Key:
package com.fedex.ground.scm.pmi.domain; import javax.persistence.Column; import javax.persistence.Embeddable; import java.io.Serializable; @Embeddable public class AccountPK implements Serializable { private static final long serialVersionUID = -849734046114464605L; public AccountPK() { } public AccountPK(String id, String applicationId) { this.id = id; this.applicationId = applicationId; } private String id; private String applicationId; @Column(name = "id") public String getId() { return id; } public void setId(String id) { this.id = id; } @Column(name = "application_fk") public String getApplicationId() { return applicationId; } public void setApplicationId(String applicationId) { this.applicationId = applicationId; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AccountPK accountPK = (AccountPK) o; if (id != null ? !id.equals(accountPK.id) : accountPK.id != null) return false; if (applicationId != null ? !applicationId.equals(accountPK.applicationId) : accountPK.applicationId != null) return false; return true; } @Override public int hashCode() { int result = id != null ? id.hashCode() : 0; result = 31 * result + (applicationId != null ? applicationId.hashCode() : 0); return result; } } // The End...
Then I created my Account Object:
package com.fedex.ground.scm.pmi.domain; import javax.persistence.*; import com.baselogic.domain.IdentifiedObjectListener; import com.baselogic.domain.StringIdentifiedObject; import org.hibernate.annotations.Type; import java.io.Serializable; @Entity @Table(name = "application_account") @IdClass(AccountPK.class) @EntityListeners({IdentifiedObjectListener.class}) public class Account extends StringIdentifiedObject implements Serializable { private String id; private String applicationId; private static final long serialVersionUID = 3832626162173359411L; private AccountPK accountPK; private Application application; private String password; private String previousPassword; private Boolean inSafe; private String environment; private String status; public Account() { super(); } @Id @Column(name = "application_fk", insertable = false, updatable = false) public String getApplicationId() { return applicationId; } public void setApplicationId(String applicationId) { this.applicationId = applicationId; } @ManyToOne @JoinColumn(name = "application_fk", insertable = false, updatable = false) public Application getApplication() { return application; } public void setApplication(Application application) { this.application = application; } // setters and getters omitted ... }// The End...
Then created my Application Object:
package com.fedex.ground.scm.pmi.domain; import java.util.LinkedList; import java.util.List; import javax.persistence.*; import org.hibernate.annotations.*; import com.baselogic.domain.StringIdentifiedObject; @Entity @Table(name = "application") public class Application extends StringIdentifiedObject { private static final long serialVersionUID = 3832626162173359411L; private List<Machine>machines=new LinkedList<Machine>(); private List<Account>accounts=new LinkedList<Account>(); private String ip; private String alias; private String status; @ManyToMany(cascade = CascadeType.MERGE) @JoinTable(name = "application_machine", joinColumns = {@JoinColumn(name = "application_id")}, inverseJoinColumns = {@JoinColumn(name = "machine_id")}) @Fetch(FetchMode.SUBSELECT) public List getMachines() { return machines; } public void setMachines(List<Machine>machines) { this.machines = machines; } @OneToMany(mappedBy = "application", cascade = CascadeType.MERGE) @JoinColumn(name = "application_fk", insertable = true, updatable = false, nullable = true) @Fetch(FetchMode.SUBSELECT) public List getAccounts() { return accounts; } public void setAccounts(Listaccounts) { this.accounts = accounts; } //<em> // setters and getters omitted ...</em> } // The End...
Then created my Machine Object:
package com.fedex.ground.scm.pmi.domain; import java.util.LinkedList; import java.util.List; import javax.persistence.*; import org.hibernate.annotations.*; import com.baselogic.domain.StringIdentifiedObject; @Entity @Table(name = "machine") public class Machine extends StringIdentifiedObject { private static final long serialVersionUID = 3832626162173359411L; private List applications = new LinkedList(); private String ip; private String alias; private String machineType; private String status; @ManyToMany(cascade = CascadeType.ALL) @JoinTable(name = "application_machine", joinColumns = {@JoinColumn(name = "machine_id")}, inverseJoinColumns = {@JoinColumn(name = "application_id")}) @Fetch(FetchMode.SUBSELECT) public List getApplications() { return applications; } public void setApplications(List Applications) { this.applications = applications; } //<em> // setters and getters omitted ...</em> } // The End...
Then I created my AccountTest Class:
package com.fedex.ground.scm.pmi.domain; import java.util.List; import com.baselogic.dao.BaseDao; import com.fedex.ground.scm.pmi.domain.Account; import org.springframework.beans.factory.*; import org.springframework.test.context.ContextConfiguration; import org.junit.Test; import static org.junit.Assert.*; @ContextConfiguration(locations = {"classpath:applicationContext-test.xml"}) public class AccountTest extends com.baselogic.test.AbstractHibernateJUnit4Test implements InitializingBean { @Autowired @Qualifier("accountDao") private BaseDao dao; @Test public void testFindAccountObject() { AccountPK pk = new AccountPK("scmbld", "pmi2"); Account account = dao.find(pk); assertNotNull(account); assertEquals("scmbld", account.getId()); assertEquals("password-here", account.getPassword()); } } // The End...
This works great and generates the needed DDL. I have tested this with H2 as well as MySql databases.
create table application ( id varchar(255) not null, date_created timestamp not null, date_updated timestamp not null, alias varchar(255), ip varchar(255), status varchar(255), primary key (id) ); create table application_account ( application_fk varchar(255) not null, id varchar(255) not null, date_created timestamp not null, date_updated timestamp not null, environment varchar(25), inSafe char(1), password varchar(50) not null, previousPassword varchar(50), status varchar(10), primary key (application_fk, id) ); create table application_machine ( machine_id varchar(255) not null, application_id varchar(255) not null ); create table machine ( id varchar(255) not null, date_created timestamp not null, date_updated timestamp not null, alias varchar(255), ip varchar(255), machineType varchar(255), status varchar(255), primary key (id) ); alter table application_account add constraint FK6A94E63EF4253111 foreign key (application_fk) references application; alter table application_machine add constraint FKE1F2A058F4253167 foreign key (application_id) references application; alter table application_machine add constraint FKE1F2A0582DDA9887 foreign key (machine_id) references machine;
My JUnit Tests
package com.fedex.ground.scm.pmi.service; import static com.baselogic.dao.criteria.Comparison.*; import com.baselogic.dao.criteria.Group; import com.fedex.ground.scm.pmi.PmiException; import com.fedex.ground.scm.pmi.domain.Account; import com.fedex.ground.scm.pmi.domain.AccountPK; import com.fedex.ground.scm.pmi.domain.Application; import com.fedex.ground.scm.pmi.service.AccountService; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import java.util.List; import static org.junit.Assert.*; import org.junit.Assert; import org.junit.Test; /** * User DAO Test. * * @author $author$ * @version $Revision$, $Date$ */ @ContextConfiguration(locations = {"classpath:applicationContext-test.xml"}) public class AccountServiceTest extends com.baselogic.test.AbstractHibernateJUnit4Test implements InitializingBean { @Autowired @Qualifier("accountService") private AccountService service; // ===== Start the Unit Tests ============================================// @Test public void testFindAllObjects() { List<Account> accounts = service.findAll(); assertNotNull(accounts); assertTrue(accounts.size() >= 1); } @Test public void testFindById() { AccountPK pk = new AccountPK("scmbld", "pmi2"); Account account = service.find(pk); assertNotNull(account); Application application = account.getApplication(); assertNotNull(application); assertEquals("pmi2", application.getId()); } @Test public void testAddNewAccount() { Account account = new Account(); account.setPassword("pmi2-password"); account.setId("testAccount"); account.setApplicationId("pmi2"); Account account2 = service.create(account); // Ensure the StringIdentifiedObjectListener sets // the following fields: assertNotNull(account2); assertNotNull(account2.getId()); assertNotNull(account2.getDateCreated()); assertNotNull(account2.getDateUpdated()); assertNotNull(account2.getApplication()); } @Test public void testUpdateAccount() { AccountPK pk = new AccountPK("scmbld", "pmi2"); Account account = service.find(pk); assertNotNull(account); // Change password assertEquals("password-here", account.getPassword()); account.setPassword("new-password"); service.update(account); assertEquals("new-password", account.getPassword()); // Change Application Application application = account.getApplication(); assertNotNull(application); assertEquals("pmi2", application.getId()); try { account = service.update("2", account); } catch (PmiException e) { fail("Should have a valid Application and not fail here."); } application = account.getApplication(); assertNotNull(application); assertEquals("2", application.getId()); } @Test public void testUpdateAccountBadApplication() { AccountPK pk = new AccountPK("scmbld", "pmi2"); Account account = service.find(pk); assertNotNull(account); try { account = service.update("BAD_APPLICATION", account); fail("Should have failed due to BAD_APPLIATION."); } catch (PmiException e) { assertNotNull(e); } } /** * Mass Update accounts * TODO: Need to test this */ //@Test public void testMassUpdateAccount() { AccountPK pk = new AccountPK("scmbld", "pmi2"); Account account = service.find(pk); assertNotNull(account); // Change password assertEquals("password-here", account.getPassword()); account.setPassword("new-password"); service.update(account); assertEquals("new-password", account.getPassword()); // Change Application Application application = account.getApplication(); assertNotNull(application); assertEquals("pmi2", application.getId()); try { account = service.update("2", account); } catch (PmiException e) { fail("Should have a valid Application and not fail here."); } application = account.getApplication(); assertNotNull(application); assertEquals("2", application.getId()); } //===== Search Tests ====================================================// @Test public void testFindByApplicationId() { try { List<Account> accounts = service.findBy(null, "pmi2", null); assertNotNull(accounts); assertEquals(2, accounts.size()); } catch (PmiException e) { fail(); } } @Test public void testFindLikeApplicationId() { try { List<Account> accounts = service.findBy(null, "%pm%", null); assertNotNull(accounts); assertEquals(2, accounts.size()); } catch (PmiException e) { fail(); } } @Test public void testFindLikeApplicationIdPre() { try { List<Account> accounts = service.findBy(null, "%pm", null); assertNotNull(accounts); assertEquals(0, accounts.size()); } catch (PmiException e) { fail(); } } @Test public void testFindLikeApplicationIdPost() { try { List<Account> accounts = service.findBy(null, "pm%", null); assertNotNull(accounts); assertEquals(2, accounts.size()); } catch (PmiException e) { fail(); } } @Test public void testFindLikeApplicationIdNoResults() { try { List<Account> accounts = service.findBy(null, "%BAD%", null); assertNotNull(accounts); assertEquals(0, accounts.size()); } catch (PmiException e) { fail(); } } @Test public void testFindByApplicationIdAndAccountId() { try { List<Account> accounts = service.findBy(null, "pmi2", "scmbld"); assertNotNull(accounts); assertEquals(1, accounts.size()); } catch (PmiException e) { fail(); } } @Test public void testFindByApplicationIdAndAccountId2() { try { List<Account> accounts = service.findBy(null, "%pmi", "scmbld"); assertNotNull(accounts); assertEquals(0, accounts.size()); } catch (PmiException e) { fail(); } } @Test public void testFindByApplicationIdAndAccountId3() { try { List<Account> accounts = service.findBy(null, "pmi%", "scmbld"); assertNotNull(accounts); assertEquals(1, accounts.size()); } catch (PmiException e) { fail(); } } @Test public void testFindByApplicationIdAndAccountId4() { try { List<Account> accounts = service.findBy(null, "pmi%", "%scm"); assertNotNull(accounts); assertEquals(0, accounts.size()); } catch (PmiException e) { fail(); } } @Test public void testFindByApplicationIdAndAccountId5() { try { List<Account> accounts = service.findBy(null, "pmi%", "scm%"); assertNotNull(accounts); assertEquals(1, accounts.size()); } catch (PmiException e) { fail(); } } } // The End...
Conclusion
to be written…
Recent Comments