View Javadoc

1   /*
2    * EL4J, the Extension Library for the J2EE, adds incremental enhancements to
3    * the spring framework, http://el4j.sf.net
4    * Copyright (C) 2008 by ELCA Informatique SA, Av. de la Harpe 22-24,
5    * 1000 Lausanne, Switzerland, http://www.elca.ch
6    *
7    * EL4J is published under the GNU Lesser General Public License (LGPL)
8    * Version 2.1. See http://www.gnu.org/licenses/
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU Lesser General Public License for more details.
14   *
15   * For alternative licensing, please contact info@elca.ch
16   */
17  package ch.elca.el4j.services.persistence.hibernate.dao.extent;
18  
19  import java.io.Serializable;
20  
21  import static ch.elca.el4j.services.persistence.hibernate.dao.extent.ExtentEntity.rootEntity;
22  
23  
24  /**
25   * A DataExtent represents the extent of the graphs of objects to be
26   * loaded in a hibernate query.<br>
27   * Be careful in not mixing up the notion of an extent:<br>
28   * In this context it is referring to the extent of the DOM to be loaded from the underlying
29   * persistent store.<br>
30   * In other contexts like for example OpenJPA the notion can have a different meaning.<br>
31   * <br>
32   * <b>Principle:</b><br>
33   * An Extent represents a part of the DOM that should be loaded together.<br>
34   * It can be used to pool together associated entities in order 
35   * to provide performance improvements over standard data fetching.<br>
36   * Specifying the extent when loading entities with Hibernate 
37   * allows for tuning of lazy loading and eager fetching behavior.<br>
38   * For details about how to use the "Fetch Type" in order to control whether a field is fetched eagerly or lazily,
39   * see the corresponding reference manual of Java Persistence API 
40   * (eg. {@link http://java.sun.com/javaee/5/docs/api/javax/persistence/FetchType.html})<br>
41   * 
42   * In a DataExtent we internally distinguish between fields, entities and collections:<br>
43   * <ul>
44   * 	<li> Fields: fields/methods in persistent class which are of a simple java type or of type string or enum.<br>
45   * 		These fields are normally stored in the same table as the root entity and cannot have any child entities.<br>
46   * 		Note that you cannot lazy-load such fields without byte-code instrumentation, thus normally all fields are
47   * 		loaded eagerly and you should not have to mention any fields in your extent.<br>
48   * 		Exception: the type {@link java.sql.Blob} and {@link java.sql.Clob} are loaded lazy per default. 
49   * 		Since there are different handling policies from different db vendors, be careful when using these types.<br>
50   * 		For example oracle takes care of the lazy loading of a blob outside a session, whereas with derby you have 
51   * 		to read out the blob during the session into for example a byte array. For an example see 
52   * 		{@link ch.elca.el4j.apps.refdb.dom.File}.
53   * 	<li> Entities: entities are the complex data types in a persistent class. Normally they get stored in a different 
54   * 		entity table and can have child entities and fields themselves. To define an entity lazy loading, specify the 
55   * 		fetch property for example in your association annotation:<br>
56   * 		<code>@OneToOne(fetch = FetchType.LAZY)</code><br>
57   * 	<li> Collections: all data types implementing the {@link java.util.Collection} interface should be
58   * 		added to the extent as collection for a proper fetching at runtime.
59   * </ul>
60   * <b>Remark:</b> When using DataExtent you don't have to have any knowledge 
61   * about the db mapping or anything like that. 
62   * It suffices to know the interface of the entity you are about to use. For example if you have:<br>
63   * <code><pre>
64   * public class Employee {
65   * 	...
66   * 	public Employee getManager() {...
67   * </pre></code>
68   * To be sure that you can access the manager of an employee after retrieving it from the dao, add the manager to 
69   * the extent you pass as an extra argument to the dao: <code>dao.findById(id, extent.with("manager"));</code><br>
70   * <br>
71   * Note that any part of the extent that is eagerly loaded according to the JPA metadata rules cannot be changed to 
72   * a lazy loading behavior with DataExtent. On the other hand, all as lazy loading indicated parts can be forced to
73   * be loaded at runtime in each query.<br>
74   * Also, parts of the extent that does not get loaded but accessed at runtime will throw a LazyLoadingException as it 
75   * would without using DataExtent's.<br>
76   * <br> 
77   * <b>Features:</b> <br>
78   *  <ul>
79   *   <li> new DataExtent: provide root class and optionally the name of the root 
80   *   <li> with/without: add/remove fields, sub-entities and/or collections to/from the extent.<br>
81   *   	If the part to add already exists in the extent, the two corresponding parts are merged.
82   *   <li> withSubentities: add sub-entities to the extent, convenient for adding entities you want
83   *   		to define in detail.
84   *   <li> all: add everything to the graph of objects.
85   *   <li> merge: merge two DataExtents to one. The class has to be the same.
86   *   <li> freeze: freeze an extent to prevent any changes to it afterwards.<br>
87   *   	Example: the predefined convenience extents in 
88   *   	{@link ch.elca.el4j.apps.refdb.dao.impl.hibernate.HibernateFileDao} and in
89   *   	{@link ch.elca.el4j.tests.person.dao.impl.hibernate.HibernatePersonDao} are frozen.
90   *   <li> see also features of {@link ExtentEntity} to write your code in a convenient way
91   *  </ul>
92   *
93   *  Remark: Be sure to import the static methods {@link ExtentEntity#entity} and 
94   *  	{@link ExtentCollection#collection} to create easily new Entities and Collections.<br> 
95   *  <br>
96   *  <b>Sample code:</b> <br>
97   * 		<code>
98   * 			<pre>
99   * 	// The Extent Object of type 'Person'
100  * 	extent = new DataExtent(Person.class);
101  * 	// Construct a complex graph:
102  * 	// Person has a List of Teeth, a Tooth has a 'Person' as owner, 
103  * 	// the owner has a list of 'Person' as friends, the friends are again
104  * 	// the same 'Person'-entity as defined in the beginning.
105  * 	extent.withSubentities(
106  *		collection("teeth",
107  *			entity(Tooth.class)
108  *				.with("owner")
109  *			),
110  *		collection("friends", extent.getRootEntity())
111  *	);
112  *	
113  *	// Extent of a File
114  *	extent = new DataExtent(File.class);
115  *	// Create a simple, light extent for an overview over the files
116  *	extent.with("name", "lastModified", "fileSize", "mimeType");
117  *	dao.getAll(extent);
118  *	// Note: there will potentially be loaded more than you specify, if it is defined to be fetched eagerly
119  *
120  *	// Another extent to see also the file content
121  *	extent = new DataExtent(File.class);
122  *	extent.with("name", "content", "lastModified", "fileSize", "mimeType");
123  *	dao.findById(id, extent);
124  *
125  *	// Extent in a reference DOM, where references are loaded lazily
126  *	extent = new DataExtent(Publication.class);
127  *	extent.all(3);
128  *	dao.findByName(publicationName, extent);
129  *	// Now you have loaded all referenced publications, books, papers, ... to a depth of 3
130  *	// Eg. using the parent of the parent is now possible.
131  *			</pre>
132  *		</code>
133  *
134  * @svnLink $Revision: 3874 $;$Date: 2009-08-04 14:25:40 +0200 (Di, 04. Aug 2009) $;$Author: swismer $;$URL: https://el4j.svn.sourceforge.net/svnroot/el4j/branches/el4j_3_1/el4j/framework/modules/hibernate/src/main/java/ch/elca/el4j/services/persistence/hibernate/dao/extent/DataExtent.java $
135  *
136  * @see ch.elca.el4j.apps.refdb.dao.impl.hibernate.HibernateFileDao for an example 
137  * 
138  * @author Andreas Rueedlinger (ARR)
139  */
140 public class DataExtent implements Serializable {
141 	/** Defines the default loading depth. */
142 	public static final int DEFAULT_LOADING_DEPTH = 1;
143 	
144 	/** The root entity. */
145 	private ExtentEntity m_rootEntity;
146 	
147 	/**
148 	 * Default Creator.
149 	 * @param c		the class of the root entity.
150 	 */
151 	public DataExtent(Class<?> c) {
152 		m_rootEntity = rootEntity(c);
153 	}
154 	
155 	/**
156 	 * Root entity of the extent.
157 	 * @return 		the root entity of the extent.
158 	 */
159 	public ExtentEntity getRootEntity() {
160 		return m_rootEntity;
161 	}
162 	
163 	/**
164 	 * The id of the data extent.
165 	 * If two extents contain the same subparts,
166 	 * this id should be equal.
167 	 * @return the id of the extent.
168 	 */
169 	public String getExtentId() {
170 		return m_rootEntity.getId();
171 	}
172 	
173 	//****************** Fluent API **********************//
174 	
175 	/**
176 	 * Extend the extent by the given fields.
177 	 * Fields are either simple properties, sub-entities or collections.
178 	 * @param fields	fields to be added.
179 	 * 
180 	 * @return the new DataExtent Object.
181 	 * @throws NoSuchMethodException 
182 	 */
183 	public DataExtent with(String... fields) throws NoSuchMethodException {
184 		m_rootEntity.with(fields);
185 		return this;
186 	}
187 	
188 	/**
189 	 * Extend the extent by the given sub-entities.
190 	 * @param entities	entities to be added.
191 	 * 
192 	 * @return the new DataExtent Object.
193 	 * @throws NoSuchMethodException 
194 	 */
195 	public DataExtent withSubentities(AbstractExtentPart... entities) throws NoSuchMethodException {
196 		m_rootEntity.withSubentities(entities);
197 		return this;
198 	}
199 	
200 	/**
201 	 * Exclude fields from the extent.
202 	 * Fields are either simple properties, sub-entities or collections.
203 	 * @param fields	fields to be excluded.
204 	 * 
205 	 * @return the new DataExtent Object.
206 	 */
207 	public DataExtent without(String...fields) {
208 		m_rootEntity.without(fields);
209 		return this;
210 	}
211 
212 	/**
213 	 * Include all fields, entities and collections of the class.
214 	 * Exploration depth is DEFAULT_LOADING_DEPTH.
215 	 * 
216 	 * @return the new DataExtent Object.
217 	 */
218 	public DataExtent all() {
219 		m_rootEntity.all(DEFAULT_LOADING_DEPTH);
220 		return this;
221 	}
222 	
223 	/**
224 	 * Include all fields, entities and collections of the class.
225 	 * @param depth		Exploration depth
226 	 * 
227 	 * @return the new DataExtent Object.
228 	 */
229 	public DataExtent all(int depth) {
230 		m_rootEntity.all(depth);
231 		return this;
232 	}
233 	
234 	/**
235 	 * Merge two DataExtents. Returns
236 	 * The class of the two rootEntities should be the same.
237 	 * @param other	the extent to be merged with.
238 	 * @return	 the union of the extents.
239 	 */
240 	public DataExtent merge(DataExtent other) {
241 		m_rootEntity.merge(other.m_rootEntity);
242 		return this;
243 	}
244 	
245 	/**
246 	 * Freeze the extent, meaning that no further changes to it are possible.
247 	 * @return the frozen extent.
248 	 */
249 	public DataExtent freeze() {
250 		m_rootEntity.freeze();
251 		return this;
252 	}
253 	
254 	/** {@inheritDoc} */
255 	@Override
256 	public int hashCode() {
257 		return getExtentId().hashCode();
258 	}
259 	
260 	/** {@inheritDoc} */
261 	@Override
262 	public boolean equals(Object object) {
263 		if (super.equals(object)) {
264 			return true;
265 		} else if (object instanceof DataExtent) {
266 			return getExtentId().equals(((DataExtent) object).getExtentId());
267 		} else {
268 			return false;
269 		}
270 	}
271 	
272 	/** {@inheritDoc} */
273 	@Override
274 	public String toString() {
275 		return getExtentId();
276 	}
277 
278 	
279 }