Mapping of collection and entity associations
Whenever you have a collection of objects (value type) in a entity class that time you need one collection table to map the collection of objects (value types) . Value type classes in the collection does not have an identifier property, So lifespan of value type instances is bounded by the owning entity.
Hibernate supports most of the JDK type collection interface like Set, SortedSet, List, Collection, Map,Sorted Map. Hibernate only wraps the collection objects which we initializes on declaration of the field.
In JPA, collection properties hold references of the entity object,collection of value type instances are not standardized.
Hibernate have the correct implementation for above declared interfaces. if you maps a collection but hibernate does not have a correct implementation of it,That time you have to tell the hibernate about its semantics. The extension point in hibernate is called PersistentCollection, you extend one of the existing classes (PersistentSet, PersistentBag or PersistentList).
how to map a collection (like set, list, map) in hibernate?
let's suppose we have collection of String (like image-name) in the item class and one item will have only image (so you won't duplicate images for one item) So in that case, we define Item class that will have set of String for images,
public class Item {
private Long itemId;
private String itemName;
private Double itemAmount;
private Date createdDate;
private Date modifiedDate;
private Set<String> images = new HashSet<String>();
// generate getter's and setters method
}
Now, we will map set of images to collection table , look at XML mapping meta data file carefully.
<hibernate-mapping package="mapping.a.set" default-access="property">
<class name="Item" table="ITEM" dynamic-insert="true" dynamic-update="true">
<id name="itemId" column="ITEM_ID">
<generator class="increment"/>
</id>
<property name="itemAmount" column="ITEM_AMOUNT"/>
<property name="itemName" column="ITEM_NAME"/>
<property name="createdDate" column="CREATED_DATE"/>
<property name="modifiedDate" column="MODIFIED_DATE"/>
<set name="images" table="ITEM_IMAGES">
<key column="ITEM_ID"/>
<element column="IMAGE_FILE" type="string" not-null="true"/>
</set>
</class>
</hibernate-mapping>
<element > tag in the mapping file declares set of images as a values type instances. Now, if you try to run the main method, it will save the image name in the collection table.
public static void main(String[] args) {
LOGGER.info("starting of main method");
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction txn = session.beginTransaction();
Item item = new Item();
item.setItemName("Samsung");
item.setItemAmount(38000D);
item.setCreatedDate(new Date());
item.setModifiedDate(new Date());
item.getImages().add("A.jpg");
LOGGER.info("before save or update");
session.saveOrUpdate(item);
txn.commit();
session.close();
HibernateUtil.shutDown();
}
see the hibernate generated comment on console.
........
Oct 25, 2012 11:00:47 AM org.hibernate.cfg.Configuration secondPassCompile
INFO: processing foreign key constraints
Oct 25, 2012 11:00:47 AM org.hibernate.cfg.Configuration secondPassCompile
INFO: processing extends queue
Oct 25, 2012 11:00:47 AM org.hibernate.cfg.Configuration secondPassCompile
INFO: processing collection mappings
Oct 25, 2012 11:00:47 AM org.hibernate.cfg.Configuration secondPassCompile
INFO: processing association property references
Oct 25, 2012 11:00:47 AM org.hibernate.cfg.Configuration secondPassCompile
INFO: processing foreign key constraints
Oct 25, 2012 11:00:47 AM org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: Running hbm2ddl schema export
Oct 25, 2012 11:00:47 AM org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: exporting generated schema to database
Oct 25, 2012 11:00:47 AM org.hibernate.connection.C3P0ConnectionProvider configure
INFO: C3P0 using driver: null at URL: jdbc:mysql://localhost:3306/HibernateTest
Oct 25, 2012 11:00:47 AM org.hibernate.connection.C3P0ConnectionProvider configure
INFO: Connection properties: {autocommit=true, user=root, password=****}
Oct 25, 2012 11:00:47 AM org.hibernate.connection.C3P0ConnectionProvider configure
INFO: autocommit mode: true
Oct 25, 2012 11:00:47 AM org.hibernate.connection.C3P0ConnectionProvider configure
WARNING: No JDBC Driver class was specified by property hibernate.connection.driver_class
11:00:47,806 INFO AbstractPoolBackedDataSource:462 - Initializing c3p0 pool... com.mchange.v2.c3p0.PoolBackedDataSource@e8d20194 [ connectionPoolDataSource -> com.mchange.v2.c3p0.WrapperConnectionPoolDataSource@475c0595 [ acquireIncrement -> 1, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, debugUnreturnedConnectionStackTraces -> false, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1bs1yqt8q15qbf2ecvl29|114a0de, idleConnectionTestPeriod -> 3000, initialPoolSize -> 5, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 300, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 20, maxStatements -> 100, maxStatementsPerConnection -> 0, minPoolSize -> 5, nestedDataSource -> com.mchange.v2.c3p0.DriverManagerDataSource@44e5bd7f [ description -> null, driverClass -> null, factoryClassLocation -> null, identityToken -> 1bs1yqt8q15qbf2ecvl29|11b8a4c, jdbcUrl -> jdbc:mysql://localhost:3306/HibernateTest, properties -> {autocommit=true, user=******, password=******} ], preferredTestQuery -> null, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false; userOverrides: {} ], dataSourceName -> null, factoryClassLocation -> null, identityToken -> 1bs1yqt8q15qbf2ecvl29|ed8181, numHelperThreads -> 3 ]
Oct 25, 2012 11:00:48 AM org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: schema export complete
Oct 25, 2012 11:00:48 AM org.hibernate.impl.SessionFactoryImpl checkNamedQueries
INFO: Checking 0 named queries
11:00:48,236 INFO HibernateTest:32 - before save or update
Hibernate: insert into CE_ITEM (ITEM_AMOUNT, ITEM_NAME, CREATED_DATE, MODIFIED_DATE, ITEM_ID) values (?, ?, ?, ?, ?)
Hibernate: insert into CE_ITEM_IMAGES (ITEM_ID, IMAGE_FILE) values (?, ?)
Oct 25, 2012 11:00:48 AM org.hibernate.impl.SessionFactoryImpl close
INFO: closing
This is used to map an ordered collection that allows duplicates element in collection. here, only we have to change two things: one is , change in item class, replace set of collection to List interface and initialise it with Array List and other is, change in XML mapping meta data, see the updates XML mapping file.
This is used to map an un ordered collection that allows duplicates element in collection. here, only we have to change two things: one is , change in item class, replace set of collection to Map interface and initialise it with Hash Map and other is, change in XML mapping meta data, see the updates XML mapping file.
how to map a collection (like set, list, map) in hibernate?
let's suppose we have collection of String (like image-name) in the item class and one item will have only image (so you won't duplicate images for one item) So in that case, we define Item class that will have set of String for images,
public class Item {
private Long itemId;
private String itemName;
private Double itemAmount;
private Date createdDate;
private Date modifiedDate;
private Set<String> images = new HashSet<String>();
// generate getter's and setters method
}
Now, we will map set of images to collection table , look at XML mapping meta data file carefully.
<hibernate-mapping package="mapping.a.set" default-access="property">
<class name="Item" table="ITEM" dynamic-insert="true" dynamic-update="true">
<id name="itemId" column="ITEM_ID">
<generator class="increment"/>
</id>
<property name="itemAmount" column="ITEM_AMOUNT"/>
<property name="itemName" column="ITEM_NAME"/>
<property name="createdDate" column="CREATED_DATE"/>
<property name="modifiedDate" column="MODIFIED_DATE"/>
<set name="images" table="ITEM_IMAGES">
<key column="ITEM_ID"/>
<element column="IMAGE_FILE" type="string" not-null="true"/>
</set>
</class>
</hibernate-mapping>
<element > tag in the mapping file declares set of images as a values type instances. Now, if you try to run the main method, it will save the image name in the collection table.
public static void main(String[] args) {
LOGGER.info("starting of main method");
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction txn = session.beginTransaction();
Item item = new Item();
item.setItemName("Samsung");
item.setItemAmount(38000D);
item.setCreatedDate(new Date());
item.setModifiedDate(new Date());
item.getImages().add("A.jpg");
LOGGER.info("before save or update");
session.saveOrUpdate(item);
txn.commit();
session.close();
HibernateUtil.shutDown();
}
see the hibernate generated comment on console.
........
Oct 25, 2012 11:00:47 AM org.hibernate.cfg.Configuration secondPassCompile
INFO: processing foreign key constraints
Oct 25, 2012 11:00:47 AM org.hibernate.cfg.Configuration secondPassCompile
INFO: processing extends queue
Oct 25, 2012 11:00:47 AM org.hibernate.cfg.Configuration secondPassCompile
INFO: processing collection mappings
Oct 25, 2012 11:00:47 AM org.hibernate.cfg.Configuration secondPassCompile
INFO: processing association property references
Oct 25, 2012 11:00:47 AM org.hibernate.cfg.Configuration secondPassCompile
INFO: processing foreign key constraints
Oct 25, 2012 11:00:47 AM org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: Running hbm2ddl schema export
Oct 25, 2012 11:00:47 AM org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: exporting generated schema to database
Oct 25, 2012 11:00:47 AM org.hibernate.connection.C3P0ConnectionProvider configure
INFO: C3P0 using driver: null at URL: jdbc:mysql://localhost:3306/HibernateTest
Oct 25, 2012 11:00:47 AM org.hibernate.connection.C3P0ConnectionProvider configure
INFO: Connection properties: {autocommit=true, user=root, password=****}
Oct 25, 2012 11:00:47 AM org.hibernate.connection.C3P0ConnectionProvider configure
INFO: autocommit mode: true
Oct 25, 2012 11:00:47 AM org.hibernate.connection.C3P0ConnectionProvider configure
WARNING: No JDBC Driver class was specified by property hibernate.connection.driver_class
11:00:47,806 INFO AbstractPoolBackedDataSource:462 - Initializing c3p0 pool... com.mchange.v2.c3p0.PoolBackedDataSource@e8d20194 [ connectionPoolDataSource -> com.mchange.v2.c3p0.WrapperConnectionPoolDataSource@475c0595 [ acquireIncrement -> 1, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, debugUnreturnedConnectionStackTraces -> false, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1bs1yqt8q15qbf2ecvl29|114a0de, idleConnectionTestPeriod -> 3000, initialPoolSize -> 5, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 300, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 20, maxStatements -> 100, maxStatementsPerConnection -> 0, minPoolSize -> 5, nestedDataSource -> com.mchange.v2.c3p0.DriverManagerDataSource@44e5bd7f [ description -> null, driverClass -> null, factoryClassLocation -> null, identityToken -> 1bs1yqt8q15qbf2ecvl29|11b8a4c, jdbcUrl -> jdbc:mysql://localhost:3306/HibernateTest, properties -> {autocommit=true, user=******, password=******} ], preferredTestQuery -> null, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false; userOverrides: {} ], dataSourceName -> null, factoryClassLocation -> null, identityToken -> 1bs1yqt8q15qbf2ecvl29|ed8181, numHelperThreads -> 3 ]
Oct 25, 2012 11:00:48 AM org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: schema export complete
Oct 25, 2012 11:00:48 AM org.hibernate.impl.SessionFactoryImpl checkNamedQueries
INFO: Checking 0 named queries
11:00:48,236 INFO HibernateTest:32 - before save or update
Hibernate: insert into CE_ITEM (ITEM_AMOUNT, ITEM_NAME, CREATED_DATE, MODIFIED_DATE, ITEM_ID) values (?, ?, ?, ?, ?)
Hibernate: insert into CE_ITEM_IMAGES (ITEM_ID, IMAGE_FILE) values (?, ?)
Oct 25, 2012 11:00:48 AM org.hibernate.impl.SessionFactoryImpl close
INFO: closing
Mapping an identifier bag :
This is used to map an unordered collection that allows duplicates element in collection. here, only we have to change two things: one is , change in item class, replace set of collection to Collection interface and initialise it with Array List and other is, change in XML mapping meta data, see the updates XML mapping file.
<hibernate-mapping package="mapping.a.set" default-access="property">
<class name="Item" table="ITEM" dynamic-insert="true" dynamic-update="true">
<id name="itemId" column="ITEM_ID">
<generator class="increment"/>
</id>
<property name="itemAmount" column="ITEM_AMOUNT"/>
<property name="itemName" column="ITEM_NAME"/>
<property name="createdDate" column="CREATED_DATE"/>
<property name="modifiedDate" column="MODIFIED_DATE"/>
<bag name="images" table="ITEM_IMAGES">
<key column="ITEM_ID" />
<element column="IMAGE_FILE" not-null="true" type="string"/>
</bag>
</class>
</hibernate-mapping>
Now you can save multiple images for one item in the database but it do not have ordering of images for one item.
Mapping a list :
<hibernate-mapping package="mapping.a.set" default-access="property">
<class name="Item" table="ITEM" dynamic-insert="true" dynamic-update="true">
<id name="itemId" column="ITEM_ID">
<generator class="increment"/>
</id>
<property name="itemAmount" column="ITEM_AMOUNT"/>
<property name="itemName" column="ITEM_NAME"/>
<property name="createdDate" column="CREATED_DATE"/>
<property name="modifiedDate" column="MODIFIED_DATE"/>
<list name="images" table="ITEM_IMAGES">
<key column="ITEM_ID"/>
<!--<index column="ITEM_INDEX" type="string"/>-->
<list-index column="ITEM_INDEX"/> <element column="IMAGE_FILE" not-null="true"
type="string"/>
</list>
<!--<index column="ITEM_INDEX" type="string"/>-->
<list-index column="ITEM_INDEX"/> <element column="IMAGE_FILE" not-null="true"
type="string"/>
</list>
</class>
</hibernate-mapping>
Now you can save multiple images for one item in the database, ordering is maintained by ITEM_INDEX column in the ITEM_IMAGE table. indexing starts with 0. you can change it with <list-index base="1"> element in your mapping file.
Note: hibernate add null elements to your java.util.list if index number in the database are not continuous.
Note: hibernate add null elements to your java.util.list if index number in the database are not continuous.
Mapping a map :
This is used to map an un ordered collection that allows duplicates element in collection. here, only we have to change two things: one is , change in item class, replace set of collection to Map interface and initialise it with Hash Map and other is, change in XML mapping meta data, see the updates XML mapping file.
<hibernate-mapping package="mapping.a.set" default-access="property">
<class name="Item" table="ITEM" dynamic-insert="true" dynamic-update="true">
<id name="itemId" column="ITEM_ID">
<generator class="increment"/>
</id>
<property name="itemAmount" column="ITEM_AMOUNT"/>
<property name="itemName" column="ITEM_NAME"/>
<property name="createdDate" column="CREATED_DATE"/>
<property name="modifiedDate" column="MODIFIED_DATE"/>
<map name="images" table="ITEM_IMAGES">
<key column="itemId"/>
<map-key column="IMAGE_NAME" type="string" />
<element column="IMAGE_FILE" type="string" not-null="true"/>
</map>
<key column="itemId"/>
<map-key column="IMAGE_NAME" type="string" />
<element column="IMAGE_FILE" type="string" not-null="true"/>
</map>
</class>
</hibernate-mapping>