Chapter 4. Collections and Associations Collections Associations 1 / 51
Java Variable and Collection class Person { Address address; class Address { Set<Person> persons = new HashSet<Person>; 2 / 51
Java Collections Java Collections Implementat ions Interfaces Hash Table Resizable Array Tree Linked List Hash Table + Linked List Set HashSet TreeSet LinkedHashSet List ArrayList LinkedList Queue Deque ArrayDeque LinkedList Map HashMap TreeMap LinkedHashMap 3 / 51
Association and Mapping Birectional Associations 4 / 51
Association and Mapping Birectional Many-to-One/One-to-Many Associations 1/3 Person * PersonAddress persons 1 address Address class Person { Address address; class Address { Set<Person> persons = new HashSet<Person>; create table Person ( personid bigint not null primary key, addressid bigint not null create table Address ( addressid bigint not null primary key 5 / 51
Association and Mapping Birectional Many-to-One/One-to-Many Associations 2/3 class Person { Address address; class Address { Set<Person> persons = new HashSet<Person>; create table Person ( personid bigint not null primary key generated by default as entity, addressid bigint not null create table Address ( addressid bigint not null primary key generated by default as entity <class name="person"> < name="" column="personid"> <generator class="native"/> </> <many-to-one name="address" class="address" column="addressid" not-null="true" /> </class> <class name="address"> < name="" column="addressid"> <generator class="native"/> </> <set name="persons" inverse="true"> <key column="addressid"/> <one-to-many class="person"/> </set> </class> 6 / 51
Association and Mapping (detail Birectional Many-to-One/One-to-Many Associations 3/3 class Person { Address address; class Address { Set<Person> persons = new HashSet<Person>; create table Person ( personid bigint not null primary key generated by default as entity, addressid bigint not null create table Address ( addressid bigint not null primary key generated by default as entity <class name="person"> < name="" column="personid"> <generator class="native"/> </> <many-to-one name="address" class="address" column="addressid" not-null="true" /> </class> <class name="address"> < name="" column="addressid"> <generator class="native"/> </> <set name="persons" inverse="true"> <key column="addressid"/> <one-to-many class="person"/> </set> </class> 7 / 51
Association and Mapping Birectional Many-to-Many Associations 1/2 Person * PersonAddress persons * addresses Address class Person { Set<Address> addresses = new HashSet<Address>; class Address { Set<Person> persons = new HashSet<Person>; create table Person ( personid bigint not null primary key, create table Address ( addressid bigint not null primary key create table PersonAddress ( personid bigint not null, addressid bigint not null, primary key (personid, addressid 8 / 51
Association and Mapping Birectional Many-to-Many Associations 2/2 class Person { Set<Address> addresses = new HashSet<Address>; class Address { Set<Person> persons = new HashSet<Person>; create table Person ( personid bigint not null primary key create table PersonAddress ( personid bigint not null, addressid bigint not null, primary key (personid, addressid create table Address ( addressid bigint not null primary key <class name="person"> < name="" column="personid"> <generator class="native"/> </> <set name="addresses" table="personaddress"> <key column="personid"/> <many-to-many class="address" column="addressid"/> </set> </class> <class name="address"> < name="" column="addressid"> <generator class="native"/> </> <set name="persons" table="personaddress" inverse="true"> <key column="addressid"/> <many-to-many class="person" column="personid"/> </set> </class> 9 / 51
Association and Mapping Birectional One-to-One Associations on a Foreign Key 1/2 Person 1 PersonAddress person 1 address Address class Person { Address address; class Address { Person person; create table Person ( personid bigint not null primary key, addressid bigint not null create table Address ( addressid bigint not null primary key 10 / 51
Association and Mapping Birectional One-to-One Associations on a Foreign Key 2/2 class Person { Address address; class Address { Person person; create table Person ( personid bigint not null primary key, addressid bigint not null create table Address ( addressid bigint not null primary key <class name="person"> < name="" column="personid"> <generator class="native"/> </> <many-to-one name="address" </class> class="address" column="addressid" unique="true" not-null="true" /> <class name="address"> < name="" column="addressid"> <generator class="native"/> </> <one-to-one name="person" </class> property-ref="address"/> property-ref (optional: the name of a property of the associated class that is joined to the primary key of this class 11 / 51 If not specified, the primary key of the associated class is used.
Association and Mapping Birectional One-to-One Associations on a Primary Key 1/2 Person 1 PersonAddress person 1 address Address class Person { Address address; class Address { Person person; create table Person ( personid bigint not null primary key, create table Address ( personid bigint not null primary key 12 / 51
Association and Mapping Birectional One-to-One Associations on a Primary Key 2/2 class Person { Address address; class Address { Person person; create table Person ( personid bigint not null primary key, create table Address ( personid bigint not null primary key <class name="person"> < name="" column="personid"> <generator class="native"/> </> <one-to-one name="address" cascade= all /> </class> <class name="address"> < name="" column="personid"> <generator class="foreign"> <param name="property">person</param> </generator> </> <one-to-one name="person" constrained="true"/> </class> constrained (optional: specifies that a foreign key constraint on the primary key of the mapped table and references the table of the associated class 13 / 51
Association and Mapping Unirectional Associations 14 / 51
Association and Mapping Unirectional Many-to-One Associations 1/2 Person * PersonAddress persons 1 address Address class Person { Address address; class Address { create table Person ( personid bigint not null primary key, addressid bigint not null create table Address ( addressid bigint not null primary key 15 / 51
Association and Mapping Unirectional Many-to-One Associations 2/2 class Person { Address address; class Address { create table Person ( personid bigint not null primary key, addressid bigint not null create table Address ( addressid bigint not null primary key <class name="person"> < name="" column="personid"> <generator class="native"/> </> <many-to-one name="address" column="addressid" not-null="true" /> </class> <class name="address"> < name="" column="addressid"> <generator class="native"/> </> </class> 16 / 51
Association and Mapping Unirectional One-to-Many Associations (unusual case 1/2 Person 1 PersonAddress * person addresses Address class Person { Set<Address> addresses; class Address { create table Person ( personid bigint not null primary key create table Address ( addressid bigint not null primary key, personid bigint not null 17 / 51
Association and Mapping Unirectional One-to-Many Associations (unusual case 2/2 class Person { Set<Address> addresses; class Address { create table Person ( personid bigint not null primary key create table Address ( addressid bigint not null primary key, personid bigint not null <class name="person"> < name="" column="personid"> <generator class="native"/> </> <set name="addresses"> <key column="personid" not-null="true"/> <one-to-many class="address"/> </set> </class> <class name="address"> < name="" column= addressid"> <generator class= native"> </> </class> 18 / 51
Association and Mapping Unirectional Many-to-Many Associations 1/2 Person * PersonAddress * persons addresses Address class Person { Set<Address> addresses = new HashSet<Address>; class Address { create table Person ( personid bigint not null primary key, create table Address ( addressid bigint not null primary key create table PersonAddress ( personid bigint not null, addressid bigint not null, primary key (personid, addressid 19 / 51
Association and Mapping Unirectional Many-to-Many Associations 2/2 class Person { Set<Address> addresses = new HashSet<Address>; create table Person ( personid bigint not null primary key, class Address { create table Address ( addressid bigint not null primary key create table PersonAddress ( personid bigint not null, addressid bigint not null, primary key (personid, addressid <class name="person"> < name="" column="personid"> <generator class="native"/> </> <set name="addresses" table="personaddress"> <key column="personid"/> <many-to-many class="address" column="addressid" /> </set> </class> <class name="address"> < name="" column= addressid"> <generator class= native"> </> </class> 20 / 51
Association and Mapping Unirectional One-to-One Associations on a Foreign Key 1/2 Person 1 PersonAddress person 1 address Address class Person { Address address; class Address { create table Person ( personid bigint not null primary key, addressid bigint not null create table Address ( addressid bigint not null primary key 21 / 51
Association and Mapping Unirectional One-to-One Associations on a Foreign Key 2/2 class Person { Address address; class Address { create table Person ( personid bigint not null primary key, addressid bigint not null create table Address ( addressid bigint not null primary key <class name="person"> < name="" column="personid"> <generator class="native"/> </> <many-to-one name="address" class="address" column="addressid" unique="true" not-null="true" /> </class> <class name="address"> < name="" column="addressid"> <generator class="native"/> </> </class> 22 / 51
Association and Mapping Unirectional One-to-One Associations on a Primary Key 1/2 Person 1 PersonAddress person 1 address Address class Person { class Address { Person person; create table Person ( personid bigint not null primary key, create table Address ( personid bigint not null primary key 23 / 51
Association and Mapping Unirectional One-to-One Associations on a Primary Key 2/2 class Person { class Address { Person person; create table Person ( personid bigint not null primary key, create table Address ( personid bigint not null primary key <class name="address"> <class name="person"> < name="" column="personid"> <generator class= native /> </> </class> < name="" column="personid"> <generator class= foreign > <param name= property >person</param> </generator> </> <one-to-one name= person" constrained= true /> </class> 24 / 51
Association and Mapping Exercise hibernate-369-mkyong-win 01-stock-quickstart-xml 03-stock-onetomany-xml 05-stock-manytomany-xml 08-stock-onetoone-xml 25 / 51
Exercise #4: Generate hbm.xml of Employee and Company ex04/src/main/java/com/oreilly/hh/data/employee.hbm.xml: <hibernate-mapping> <class name="com.oreilly.hh.data.employee" > < name="" > </> </class> </hibernate-mapping> ex04/src/main/java/com/oreilly/hh/data/company.hbm.xml: <hibernate-mapping> <class name="com.oreilly.hh.data.company" > < name="" > </> </class> </hibernate-mapping> 26 / 51
Exercise #4: test $ gradle test :ex04:test Results: SUCCESS (3 tests, 3 successes, 0 failures, 0 skipped BUILD SUCCESSFUL 27 / 51
Java Collections vs Database Relationships Birectional Many-to-Many Associations Track UML name Artist 0..* artists track_artists 0..* tracks title filepath playtime added volume Java class Artist { Integer ; String name; Set<Track> tracks; class Track { Integer ; String title; String filepath; time playtime; date added; short volume; Set<Artist> artists; 28 / 51
Java Collections vs Database Relationships Unirectional Many-to-Many Associations Track UML name Artist track_artists 0..* tracks title filepath playtime added volume Java class Artist { Integer ; String name; Set<Track> tracks; class Track { Integer ; String title; String filepath; time playtime; date added; short volume; 29 / 51
Java Collections vs Database Relationships Many-to-Many Associations and Database Relationships Track UML name Artist 0..* artists track_artists 0..* tracks title filepath playtime added volume Table create table ARTIST ( integer primary key, name varchar(62 create table TRACK_ARTISTS ( artist_ integer, track_ integer, foreign key (artist_ references artist(, foreign key (track_ references track( create table TRACK ( integer primary key, title varchar(62, filepath varchar(62, playtime time, added date, volume smallint 30 / 51
Mapping Collections (for Birectional Many-to-Many Association name Artist 0..* artists track_artists 0..* tracks Track title filepath playtime added volume <set name="tracks" table="track_artists" inverse="true"> <key column="artist_id /> <many-to-many class="com.oreilly.hh.data.track" column="track_id"/> </set> <set name="artists" table="track_artists"> <key column="track_id"/> <many-to-many class="com.oreilly.hh.data.artist" column="artist_id"/> </set> 31 / 51
Mapping Collections (for Unirectional Many-to-Many Association name Artist track_artists 0..* tracks Track title filepath playtime added volume <set name="tracks" table="track_artists"> <key column="artist_id /> <many-to-many class="com.oreilly.hh.data.track" column="track_id"/> </set> 32 / 51
Mapping Collections (Artist.hbm.xml <?xml version="1.0 encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC...> <hibernate-mapping> <class name="com.oreilly.hh.data.artist" table="artist"> <meta attribute="class-description">... </meta> < name="" type="int" column="artist_id"> <generator class="native"/> </> <property name="name" type="string"> <column name="name" not-null="true" unique="true" index="artist_name"/> </property> <set name="tracks" table="track_artists" inverse="true"> <key column="artist_id"/> <many-to-many class="com.oreilly.hh.data.track" column="track_id"/> </set> </class> </hibernate-mapping> 33 / 51
Mapping Collections (Track.hbm.xml <?xml version="1.0 encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC...> <hibernate-mapping> <class name="com.oreilly.hh.data.track" table="track">... <set name="artists" table="track_artists"> <key column="track_id"/> <many-to-many class="com.oreilly.hh.data.artist" column="artist_id"/> </set>... </class> </hibernate-mapping> 34 / 51
Mapping Collections (hibernate.cfg.xml hibernate.cfg.xml : <?xml version="1.0 encoding="utf-8"?> <!DOCTYPE hibernate-configuration PUBLIC...> <hibernate-configuration> <session-factory>... <mapping resource="com/oreilly/hh/data/track.hbm.xml"/> <mapping resource="com/oreilly/hh/data/artist.hbm.xml"/> </session-factory> </hibernate-configuration> 35 / 51
Persisting Collections (CreateTest.java public class CreateTest { public static Artist getartist(string name, boolean create, Session session { Query query = session.getnamedquery("com.oreilly.hh.artistbyname"; query.setstring("name", name; Artist found = (Artistquery.uniqueResult(; if (found == null && create { found = new Artist(name, new HashSet<Track>(; session.save(found; return found; /** * Utility method to associate an artist with a track */ private static vo addtrackartist(track track, Artist artist { track.getartists(.add(artist;... 36 / 51
Persisting Collections (Artist.hbm.xml Artist.hbm.xml :... <query name="com.oreilly.hh.artistbyname"> <![CDATA[ from Artist as artist where upper(artist.name = upper(:name ]]> </query>... Track.hbm.xml :... <query name="com.oreilly.hh.tracksnolongerthan"> <![CDATA[ from Track as track where track.playtime <= :length ]]> </query>... 37 / 51
Persisting Collections (CreateTest.java... public static vo main(string args[] throws Exception {... try { // Create some data and persist it tx = session.begintransaction(; Track track = new Track("Russian Trance", "vol2/album610/track02.mp3", Time.valueOf("00:03:30", new HashSet<Artist>(, new Date(, (short0; addtrackartist(track, getartist("ppk", true, session; session.save(track; track = new Track("Veo Killed the Radio Star", "vol2/album611/track12.mp3", Time.valueOf("00:03:49", new HashSet<Artist>(, new Date(, (short0; addtrackartist(track, getartist("the Buggles", true, session; session.save(track; track = new Track("Gravity's Angel", "vol2/album175/track03.mp3", Time.valueOf("00:06:06", new HashSet<Artist>(, new Date(, (short0; addtrackartist(track, getartist("laurie Anderson", true, session; session.save(track; 38 / 51
Persisting Collections (CreateTest.java... track = new Track("Adagio for Strings (Ferry Corsten Remix", "vol2/album972/track01.mp3", Time.valueOf("00:06:35", new HashSet<Artist>(, new Date(, (short0; addtrackartist(track, getartist("william Orbit", true, session; addtrackartist(track, getartist("ferry Corsten", true, session; addtrackartist(track, getartist("samuel Barber", true, session; session.save(track; track = new Track("Adagio for Strings (ATB Remix", "vol2/album972/track02.mp3", Time.valueOf("00:07:39", new HashSet<Artist>(, new Date(, (short0; addtrackartist(track, getartist("william Orbit", true, session; addtrackartist(track, getartist("atb", true, session; addtrackartist(track, getartist("samuel Barber", true, session; session.save(track; track = new Track("The World '99", "vol2/singles/pvw99.mp3", Time.valueOf("00:07:05", new HashSet<Artist>(, new Date(, (short0; addtrackartist(track, getartist("pulp Victim", true, session; addtrackartist(track, getartist("ferry Corsten", true, session; session.save(track; track = new Track("Test Tone 1", "vol2/singles/test01.mp3", session.save(track;... Time.valueOf("00:00:10", new HashSet<Artist>(, new Date(, (short0; 39 / 51
Persisting Collections : What just happened? $ gradle ctest $ gradle db 40 / 51
Retrieving Collections (QueryTest.java package com.oreilly.hh;... public class QueryTest {... public static String listartistnames(set<artist> artists { StringBuilder result = new StringBuilder(; for (Artist artist : artists { result.append((result.length( == 0? "(" : ", "; result.append(artist.getname(; if (result.length( > 0 { result.append(" "; return result.tostring(;... 41 / 51
Retrieving Collections (QueryTest.java public class QueryTest {... public static String listartistnames(set<artist> artists {... public static vo main(string args[] throws Exception {... try { // Print the tracks that will fit in seven minutes List tracks = tracksnolongerthan(time.valueof("00:07:00", session;... for ( Track atrack : tracks { System.out.println("Track: \"" + atrack.gettitle( + "\" " + listartistnames(atrack.getartists( + atrack.getplaytime(; 42 / 51
Retrieving Collections (Hibernate.cfg.xml... <!-- Echo all executed SQL to stdout --> <property name="show_sql">false</property>... 43 / 51
Retrieving Collections : QueryTest Output $ gradle qtest :ch04:compilejava UP-TO-DATE :ch04:processresources UP-TO-DATE :ch04:classes UP-TO-DATE :ch04:qtest Track: "Russian Trance" (PPK 00:03:30 Track: "Veo Killed the Radio Star" (The Buggles 00:03:49 Track: "Gravity's Angel" (Laurie Anderson 00:06:06 Track: "Adagio for Strings (Ferry Corsten Remix" (Ferry Corsten, William Orbit, Samuel Ba rber 00:06:35 Track: "Test Tone 1" 00:00:10 Comment: Pink noise to test equalization BUILD SUCCESSFUL Total time: 2.299 secs 44 / 51
Using Birectional Associations (QueryTest2.java // Example 4-12. Source for QueryTest2.java public class QueryTest2 extends JPanel {... private vo updatetracks(string name { model.removeallelements(; // Clear out previous tracks if (name.length( < 1 return; // Nothing to do try { // Ask for a session using the JDBC information we've configured Session session = sessionfactory.opensession(; try { Artist artist = CreateTest.getArtist(name, false, session; if (artist == null { // Unknown artist model.addelement("artist not found"; return; // List the tracks associated with the artist for (Track atrack : artist.gettracks( { model.addelement("track: \"" + atrack.gettitle( + "\", " + atrack.getplaytime(; finally { session.close(; catch (Exception e { System.err.println("Problem updating tracks:" + e; e.printstacktrace(; 45 / 51
Using Birectional Associations (build.gradle... task qtest2(dependson: classes, type: JavaExec { main = 'com.oreilly.hh.querytest2' classpath = sourcesets.main.runtimeclasspath $ gradle qtest2 46 / 51
Working with Simpler Collections 47 / 51
Working with Simpler Collections Collections of associations to other objects collections of simple values, like strings, numbers, and nonpersistent value classes want to record some number of comments about each track in the database Track.hbm.xml... <set name="comments" table="track_comments"> <key column="track_id"/> <element column="comment" type="string"/> </set> gradle schema create table TRACK_COMMENTS (TRACK_ID integer not null, COMMENT varchar(255; alter table TRACK_COMMENTS add constraint FK105B26882DCBFAB5 foreign key (TRACK_ID references TRACK; 48 / 51
Working with Simpler Collections Add Comment Set at the end of each Constructor of CreateTest.java : track = new Track("Test Tone 1", "vol2/singles/test01.mp3", Time.valueOf("00:00:10", new HashSet<Artist>(, new Date(, (short0, new HashSet<String>(; Then assign a comment on the following line : track.getcomments(.add("pink noise to test equalization"; 49 / 51
Working with Simpler Collections Add another loop after the track println( in QueryTest.java to print the comments for the track : for (String comment : atrack.getcomments( { System.out.println(" Comment: " + comment; gradle qtest... Track: "Test Tone 1" 00:00:10 Comment: Pink noise to test equalization 50 / 51
Materials for Further Study Hibernate Home http://www.hibernate.org/ Hibernate Manual Hibernate Getting Started Gue 3.6 http://docs.jboss.org/hibernate/core/3.6/quickstart/en-us/html/ Hibernate Reference Documentation 3.6 http://docs.jboss.org/hibernate/core/3.6/reference/en-us/html/ http://docs.jboss.org/hibernate/core/3.6/reference/en-us/pdf/hibernate_reference.pdf Hibernate Reference Documentation 4.3 and 5.0 Hibernate Tutorial http://www.mkyong.com/tutorials/hibernate-tutorials/ 51 / 51