1 | <?xml version="1.0" encoding="UTF-8"?> |
---|
2 | <!DOCTYPE chapter PUBLIC |
---|
3 | "-//Dawid Weiss//DTD DocBook V3.1-Based Extension for XML and graphics inclusion//EN" |
---|
4 | "../../../../lib/docbook/preprocess/dweiss-docbook-extensions.dtd"> |
---|
5 | <!-- |
---|
6 | $Id: core_ref.xml 5027 2009-07-28 09:29:51Z nicklas $ |
---|
7 | |
---|
8 | Copyright (C) 2007 Peter Johansson, Nicklas Nordborg, Martin Svensson |
---|
9 | |
---|
10 | This file is part of BASE - BioArray Software Environment. |
---|
11 | Available at http://base.thep.lu.se/ |
---|
12 | |
---|
13 | BASE is free software; you can redistribute it and/or |
---|
14 | modify it under the terms of the GNU General Public License |
---|
15 | as published by the Free Software Foundation; either version 3 |
---|
16 | of the License, or (at your option) any later version. |
---|
17 | |
---|
18 | BASE is distributed in the hope that it will be useful, |
---|
19 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
21 | GNU General Public License for more details. |
---|
22 | |
---|
23 | You should have received a copy of the GNU General Public License |
---|
24 | along with BASE. If not, see <http://www.gnu.org/licenses/>. |
---|
25 | --> |
---|
26 | |
---|
27 | <chapter id="core_ref"> |
---|
28 | <?dbhtml dir="core_ref"?> |
---|
29 | <title>Core developer reference</title> |
---|
30 | |
---|
31 | <sect1 id="core_ref.release"> |
---|
32 | <title>Publishing a new release</title> |
---|
33 | <para> |
---|
34 | This documentation is available on the <ulink url="http://base.thep.lu.se/wiki/ReleaseProcedure">BASE wiki</ulink>. |
---|
35 | </para> |
---|
36 | </sect1> |
---|
37 | |
---|
38 | <sect1 id="core_ref.build"> |
---|
39 | <title>Subversion / building BASE</title> |
---|
40 | <para> |
---|
41 | This documentation is only available in the old format. |
---|
42 | See <ulink url="http://base.thep.lu.se/chrome/site/doc/historical/development/build.html" |
---|
43 | >http://base.thep.lu.se/chrome/site/doc/historical/development/build.html</ulink> |
---|
44 | </para> |
---|
45 | </sect1> |
---|
46 | |
---|
47 | <sect1 id="core_ref.rules" chunked="1"> |
---|
48 | <title>Coding rules and guidelines</title> |
---|
49 | |
---|
50 | <sect2 id="core_ref.rules.devprocess"> |
---|
51 | <title>Development process and other important procedures</title> |
---|
52 | <para> |
---|
53 | This section describes the development process we try to use in the BASE project. |
---|
54 | It is not carved in stone and deviations may occur. For every new feature |
---|
55 | or enhancement the procure below should be followed. |
---|
56 | If you encounter any problems, arrange a group meeting. Someone else |
---|
57 | may have the solution! The text is biased towards adding new |
---|
58 | items to BASE, but it should be possible to use the general outline |
---|
59 | even for other types of features. |
---|
60 | </para> |
---|
61 | |
---|
62 | <bridgehead>1. Group meeting</bridgehead> |
---|
63 | <itemizedlist> |
---|
64 | <listitem> |
---|
65 | <para> |
---|
66 | The group should have a short meeting and discuss the new or changed |
---|
67 | feature. Problem areas should be identified, not solved! |
---|
68 | </para> |
---|
69 | </listitem> |
---|
70 | <listitem> |
---|
71 | <para> |
---|
72 | The person who is going to make the analysis, design and development is |
---|
73 | responsible for taking notes. They should be kept until the analysis |
---|
74 | and design phase has been finished. |
---|
75 | </para> |
---|
76 | </listitem> |
---|
77 | <listitem> |
---|
78 | <para> |
---|
79 | A follow-up meeting should be held at the end of the analysis phase. |
---|
80 | </para> |
---|
81 | </listitem> |
---|
82 | <listitem> |
---|
83 | <para> |
---|
84 | A single meeting may of course discuss more than one feature. |
---|
85 | </para> |
---|
86 | </listitem> |
---|
87 | </itemizedlist> |
---|
88 | |
---|
89 | <bridgehead>2. Analysis and design</bridgehead> |
---|
90 | <itemizedlist> |
---|
91 | <listitem> |
---|
92 | <para> |
---|
93 | Create an diagram of the classes including their properties, links and associations. |
---|
94 | Use the already existing diagrams and code as a template. |
---|
95 | The diagram should have information about cache and proxy settings. |
---|
96 | </para> |
---|
97 | </listitem> |
---|
98 | <listitem> |
---|
99 | <para> |
---|
100 | Write a short document about the diagram, especially things that are not obvious |
---|
101 | and explain any deviations from the recommendations in the coding guidelines. |
---|
102 | </para> |
---|
103 | </listitem> |
---|
104 | <listitem> |
---|
105 | <para> |
---|
106 | Identify things that may affect backwards compatibility. For more |
---|
107 | information about such things read <xref linkend="api_overview.public_api" /> |
---|
108 | and <xref linkend="core_ref.rules.compatibility" />. |
---|
109 | </para> |
---|
110 | </listitem> |
---|
111 | <listitem> |
---|
112 | <para> |
---|
113 | Identify what parts of the documentation that needs to changed or added |
---|
114 | to describe the new feature. This includes, but is not limited to: |
---|
115 | <itemizedlist> |
---|
116 | <listitem> |
---|
117 | <para> |
---|
118 | User and administrator documentation, how to use the feature, screenshots, |
---|
119 | etc. |
---|
120 | </para> |
---|
121 | </listitem> |
---|
122 | <listitem> |
---|
123 | <para> |
---|
124 | Plug-in and core developer documentation, code examples, database schema changes, |
---|
125 | etc. |
---|
126 | </para> |
---|
127 | </listitem> |
---|
128 | </itemizedlist> |
---|
129 | </para> |
---|
130 | </listitem> |
---|
131 | <listitem> |
---|
132 | <para> |
---|
133 | If there are any problems with the existing code, these should be solved at |
---|
134 | this stage. Write some prototype code for testing if necessary. |
---|
135 | </para> |
---|
136 | </listitem> |
---|
137 | <listitem> |
---|
138 | <para> |
---|
139 | Group meeting to verify that the specified solution is ok, and to make sure |
---|
140 | everybody has enough knowledge of the solution. |
---|
141 | </para> |
---|
142 | </listitem> |
---|
143 | </itemizedlist> |
---|
144 | |
---|
145 | <bridgehead>3. Create the classes for the data layer</bridgehead> |
---|
146 | <itemizedlist> |
---|
147 | <listitem> |
---|
148 | <para> |
---|
149 | If step 2 is properly done, this should not take long. |
---|
150 | </para> |
---|
151 | </listitem> |
---|
152 | <listitem> |
---|
153 | <para> |
---|
154 | Follow the coding guidelines in <xref linkend="core_ref.rules.datalayer" />. |
---|
155 | </para> |
---|
156 | </listitem> |
---|
157 | <listitem> |
---|
158 | <para> |
---|
159 | At the end of this step, go back and have a lock at the diagram/documentation |
---|
160 | from the analysis and design phase and make sure everything is still correct. |
---|
161 | </para> |
---|
162 | </listitem> |
---|
163 | </itemizedlist> |
---|
164 | |
---|
165 | <bridgehead>4. Create the corresponding classes in the core layer</bridgehead> |
---|
166 | <itemizedlist> |
---|
167 | <listitem> |
---|
168 | <para> |
---|
169 | For simple cases this is also easy. Other cases may require more effort. |
---|
170 | </para> |
---|
171 | </listitem> |
---|
172 | <listitem> |
---|
173 | <para> |
---|
174 | If needed, go back to the analysis and design phase and do some more investigations. |
---|
175 | Make sure the documentation is updated if there are changes. |
---|
176 | </para> |
---|
177 | </listitem> |
---|
178 | </itemizedlist> |
---|
179 | |
---|
180 | <bridgehead>5. Create test code</bridgehead> |
---|
181 | <itemizedlist> |
---|
182 | <listitem> |
---|
183 | <para> |
---|
184 | Build on and use the existing test as much as possible. |
---|
185 | </para> |
---|
186 | </listitem> |
---|
187 | </itemizedlist> |
---|
188 | |
---|
189 | <bridgehead>6. Write code to update existing installations</bridgehead> |
---|
190 | <important> |
---|
191 | <itemizedlist> |
---|
192 | <listitem> |
---|
193 | <para> |
---|
194 | If the database schema is changed or if there for some reason is need to update |
---|
195 | existing data in the database, the <constant>Install.SCHEMA_VERSION</constant> |
---|
196 | counter must be increased. |
---|
197 | </para> |
---|
198 | </listitem> |
---|
199 | <listitem> |
---|
200 | <para> |
---|
201 | Add code to the <classname docapi="net.sf.basedb.core">net.sf.basedb.core.Update</classname> class |
---|
202 | to increase the schema version and modify data in existing installations. |
---|
203 | </para> |
---|
204 | </listitem> |
---|
205 | </itemizedlist> |
---|
206 | </important> |
---|
207 | |
---|
208 | <bridgehead>7. Write new and update existing user documentation</bridgehead> |
---|
209 | <itemizedlist> |
---|
210 | <listitem> |
---|
211 | <para> |
---|
212 | Most likely, users and plug-in developers wants to know about the feature. |
---|
213 | </para> |
---|
214 | </listitem> |
---|
215 | </itemizedlist> |
---|
216 | |
---|
217 | <important> |
---|
218 | <para> |
---|
219 | Do not forget to update the <xref linkend="appendix.incompatible" /> document |
---|
220 | if you have introduced any incomaptible changes. |
---|
221 | </para> |
---|
222 | </important> |
---|
223 | |
---|
224 | </sect2> |
---|
225 | <sect2 id="core_ref.rules.style"> |
---|
226 | <title>General coding style guidelines</title> |
---|
227 | <para> |
---|
228 | This documentation is only available in the old format. |
---|
229 | See <ulink url="http://base.thep.lu.se/chrome/site/doc/historical/development/coding/generic.html" |
---|
230 | >http://base.thep.lu.se/chrome/site/doc/historical/development/coding/generic.html</ulink> |
---|
231 | </para> |
---|
232 | </sect2> |
---|
233 | |
---|
234 | <sect2 id="core_ref.rules.compatibility"> |
---|
235 | <title>API changes and backwards compatibility</title> |
---|
236 | <para> |
---|
237 | The main rule is to do not introduce any changes that are |
---|
238 | backwards incompatible. That is, existing client applications |
---|
239 | and plug-ins should continue to run in the next release of BASE, |
---|
240 | without the need to change them. It may sound easy but there are |
---|
241 | many things to watch out for. |
---|
242 | </para> |
---|
243 | |
---|
244 | <important> |
---|
245 | <title>Do not forget to log changes!</title> |
---|
246 | <para> |
---|
247 | Any change that may affect backwards compatibility must be logged in |
---|
248 | <xref linkend="appendix.incompatible" />. |
---|
249 | </para> |
---|
250 | </important> |
---|
251 | |
---|
252 | <para> |
---|
253 | There is a great article about this subject on <ulink |
---|
254 | url="http://wiki.eclipse.org/index.php/Evolving_Java-based_APIs" |
---|
255 | >http://wiki.eclipse.org/index.php/Evolving_Java-based_APIs</ulink>. |
---|
256 | This is what we will try to comply with. |
---|
257 | </para> |
---|
258 | |
---|
259 | <sect3 id="core_ref.rules.compatibility.public_api"> |
---|
260 | <title>Does the changes affect the Public API?</title> |
---|
261 | |
---|
262 | <para> |
---|
263 | See <xref linkend="api_overview.public_api" /> and |
---|
264 | <ulink url="http://base.thep.lu.se/chrome/site/doc/api/index.html" |
---|
265 | >the javadoc</ulink>for information |
---|
266 | about the public API. |
---|
267 | </para> |
---|
268 | |
---|
269 | <para> |
---|
270 | Changes made to the non-public API does not have to follow the |
---|
271 | same rules. |
---|
272 | </para> |
---|
273 | </sect3> |
---|
274 | |
---|
275 | <sect3 id="core_ref.rules.compatibility.contract"> |
---|
276 | <title>Contract compatibility</title> |
---|
277 | |
---|
278 | <para> |
---|
279 | TODO |
---|
280 | </para> |
---|
281 | |
---|
282 | </sect3> |
---|
283 | |
---|
284 | <sect3 id="core_ref.rules.compatibility.binary"> |
---|
285 | <title>Binary compatibility</title> |
---|
286 | |
---|
287 | <para> |
---|
288 | TODO |
---|
289 | </para> |
---|
290 | |
---|
291 | </sect3> |
---|
292 | |
---|
293 | <sect3 id="core_ref.rules.compatibility.data"> |
---|
294 | <title>Internal data structure compatibility</title> |
---|
295 | |
---|
296 | <para> |
---|
297 | TODO |
---|
298 | </para> |
---|
299 | |
---|
300 | </sect3> |
---|
301 | |
---|
302 | <sect3 id="core_ref.rules.compatibility.source"> |
---|
303 | <title>Source code compatibility</title> |
---|
304 | |
---|
305 | <para> |
---|
306 | TODO |
---|
307 | </para> |
---|
308 | |
---|
309 | </sect3> |
---|
310 | |
---|
311 | </sect2> |
---|
312 | |
---|
313 | <sect2 id="core_ref.rules.datalayer"> |
---|
314 | <title>Data-layer rules</title> |
---|
315 | |
---|
316 | <para> |
---|
317 | The coding guidelines for this package has been slightly modified from the |
---|
318 | the general coding guidelines. Here is a short list with the changes. |
---|
319 | </para> |
---|
320 | |
---|
321 | <sect3 id="core_ref.rules.datalayer.methodorder"> |
---|
322 | <title>Attributes and methods order</title> |
---|
323 | <para> |
---|
324 | Inside a class, attributes and methods should be organised in related groups, |
---|
325 | ie. the private attribute is together with the getter and setter methods that uses |
---|
326 | that attribute. This makes it easy to re-use existing code with copy-and-paste |
---|
327 | operations. |
---|
328 | </para> |
---|
329 | |
---|
330 | <programlisting language="java"> |
---|
331 | public static int long MAX_ADDRESS_LENGTH = 255; |
---|
332 | private String address; |
---|
333 | /** |
---|
334 | @hibernate.property column="`address`" type="string" length="255" not-null="false" |
---|
335 | */ |
---|
336 | public String getAddress() |
---|
337 | { |
---|
338 | return address; |
---|
339 | } |
---|
340 | public void setAddress(String address) |
---|
341 | { |
---|
342 | this.address = address; |
---|
343 | } |
---|
344 | |
---|
345 | private int row; |
---|
346 | /** |
---|
347 | @hibernate.property column="`row`" type="int" |
---|
348 | */ |
---|
349 | public int getRow() |
---|
350 | { |
---|
351 | return row; |
---|
352 | } |
---|
353 | public void setRow(int row) |
---|
354 | { |
---|
355 | this.row = row; |
---|
356 | } |
---|
357 | </programlisting> |
---|
358 | </sect3> |
---|
359 | |
---|
360 | <sect3 id="core_ref.rules.datalayer.classnames"> |
---|
361 | <title>Class and interface names</title> |
---|
362 | <para> |
---|
363 | Class names should follow the general guidelines, but should in most |
---|
364 | cases end with <classname>Data</classname>. |
---|
365 | </para> |
---|
366 | <programlisting language="java"> |
---|
367 | public class SampleData |
---|
368 | extends CommonData |
---|
369 | implements DiskConsumableData |
---|
370 | { |
---|
371 | ... |
---|
372 | } |
---|
373 | </programlisting> |
---|
374 | </sect3> |
---|
375 | <sect3 id="core_ref.rules.datalayer.basicclasses"> |
---|
376 | <title>Extend/implement the basic classes and interfaces</title> |
---|
377 | <para> |
---|
378 | Each data-class must inherit from one of the already existing abstract base classes. |
---|
379 | They contain code that is common to all classes, for example implementations of |
---|
380 | the <methodname>equals()</methodname> and <methodname>hashCode()</methodname> |
---|
381 | methods or how to link with the owner of an item. For information about |
---|
382 | which classes/interfaces that can be used see <xref linkend="data_api.basic" />. |
---|
383 | </para> |
---|
384 | </sect3> |
---|
385 | |
---|
386 | <sect3 id="core_ref.rules.datalayer.constructor"> |
---|
387 | <title>Define a public no-argument constructor</title> |
---|
388 | <para> |
---|
389 | Always define a public a no-argument constructor. No other constructors are needed. |
---|
390 | If we want to use other persistence mechanisms or serializability in the future |
---|
391 | this type of constructor is probably the most compatible. The constructor should |
---|
392 | be empty and not contain any code. Do not initialise properties or create new objects |
---|
393 | for internal use. Most of the time the object is loaded by |
---|
394 | Hibernate and Hibernate will ensure that it is properly initialised by calling |
---|
395 | all setter methods. |
---|
396 | </para> |
---|
397 | |
---|
398 | <para> |
---|
399 | For example, a many-to-many relation usually has a <interfacename>Set</interfacename> |
---|
400 | or a <interfacename>Map</interfacename> to hold the links to the other objects. Do not |
---|
401 | create a new <classname>HashSet</classname> or <classname>HashMap</classname> |
---|
402 | in the constructor. Wait until the get method is called and only create a new |
---|
403 | object if Hibernate hasn't already called the setter method with it's own object. |
---|
404 | See the code example below. There is also more information about this in |
---|
405 | <xref linkend="core_ref.rules.datalayer.manytomany" />. |
---|
406 | </para> |
---|
407 | |
---|
408 | <programlisting language="java"> |
---|
409 | // From GroupData.java |
---|
410 | public GroupData() |
---|
411 | {} |
---|
412 | |
---|
413 | private Set<UserData> users; |
---|
414 | public Set<UserData> getUsers() |
---|
415 | { |
---|
416 | if (users == null) users = new HashSet<UserData>(); |
---|
417 | return users; |
---|
418 | } |
---|
419 | </programlisting> |
---|
420 | |
---|
421 | <para> |
---|
422 | See also: |
---|
423 | </para> |
---|
424 | |
---|
425 | <itemizedlist> |
---|
426 | <listitem> |
---|
427 | <para> |
---|
428 | "Hibernate in action", chapter 3.2.3 "Writing POJOs", page 67-69 |
---|
429 | </para> |
---|
430 | </listitem> |
---|
431 | <listitem> |
---|
432 | <para> |
---|
433 | Hibernate user documentation: <ulink |
---|
434 | url="http://www.hibernate.org/hib_docs/reference/en/html/persistent-classes.html#persistent-classes-pojo-constructor">4.1.1. |
---|
435 | Implement a no-argument constructor</ulink> |
---|
436 | </para> |
---|
437 | </listitem> |
---|
438 | </itemizedlist> |
---|
439 | |
---|
440 | </sect3> |
---|
441 | |
---|
442 | <sect3 id="core_ref.rules.datalayer.identity"> |
---|
443 | <title>Object identity</title> |
---|
444 | <para> |
---|
445 | We use database identity to compare objects, ie. two objects are considered |
---|
446 | equal if they are of the same class and have the same id, thus representing the |
---|
447 | same database row. All this stuff is implemented by the BasicData class. Therefore |
---|
448 | it is required that all classes are subclasses of this class. It is recommended |
---|
449 | that the <methodname>equals()</methodname> or <methodname>hashCode()</methodname> |
---|
450 | methods are not overridden by any of the subclasses. We would have liked to make |
---|
451 | them final, but then the proxy feature of Hibernate would not work. |
---|
452 | </para> |
---|
453 | |
---|
454 | <warning> |
---|
455 | <title>Avoid mixing saved and unsaved objects</title> |
---|
456 | <para> |
---|
457 | The approch used for object identity may give us a problem if we mix objects |
---|
458 | which hasn't been saved to the database, with objects loaded from the database. |
---|
459 | Our recommendation is to avoid that, and save any objects to the database before |
---|
460 | adding them to sets, maps or any other structure that uses the |
---|
461 | <methodname>equals()</methodname> and <methodname>hashCode()</methodname> methods. |
---|
462 | </para> |
---|
463 | <para> |
---|
464 | To be more specific, the problem arises because the following two rules for |
---|
465 | hascodes are contradicting when the hashcode is based on the database id: |
---|
466 | </para> |
---|
467 | <orderedlist> |
---|
468 | <listitem> |
---|
469 | <para> |
---|
470 | The hash code of an object mustn't change |
---|
471 | </para> |
---|
472 | </listitem> |
---|
473 | <listitem> |
---|
474 | <para> |
---|
475 | Equal objects must have equal hash code |
---|
476 | </para> |
---|
477 | </listitem> |
---|
478 | </orderedlist> |
---|
479 | <para> |
---|
480 | For objects in the database, the hash code is based on the id. For new objects, |
---|
481 | which doesn't have an id yet, we fall back to the system hash code. But, what |
---|
482 | happens when we save the new object to the database? If nobody has asked for |
---|
483 | the hash code it is safe to use the id, otherwise we must stick with the system |
---|
484 | hash code. Now, imagine that we load the same object from the database in |
---|
485 | another Hibernate session. What will now happen? The loaded object will have |
---|
486 | it's hash code based on the id but the original object is still using the |
---|
487 | system hash code, which most likely is not the same as the id. Yet, the |
---|
488 | <methodname>equals()</methodname> method returns true. This is a violation |
---|
489 | of the contract for the equals method. If these two objects are used in a set |
---|
490 | it may cause unexpected behaviour. Therefore, do not put new objects in a |
---|
491 | set, or other collection, that calls the <methodname>hashCode()</methodname> |
---|
492 | method before the object is saved to the database. |
---|
493 | </para> |
---|
494 | </warning> |
---|
495 | |
---|
496 | <para> |
---|
497 | See also: |
---|
498 | </para> |
---|
499 | <itemizedlist> |
---|
500 | <listitem> |
---|
501 | <para> |
---|
502 | "Hibernate in action", chapter 3.4 "Understanding object identity", page 87-90 |
---|
503 | </para> |
---|
504 | </listitem> |
---|
505 | <listitem> |
---|
506 | <para> |
---|
507 | "Hibernate in action", chapter 4.1.4 "The scope of object identity", page 119-121 |
---|
508 | </para> |
---|
509 | </listitem> |
---|
510 | <listitem> |
---|
511 | <para> |
---|
512 | "Hibernate in action", chapter 4.1.6 "Implementing equals() and hashCode(), page 122-126 |
---|
513 | </para> |
---|
514 | </listitem> |
---|
515 | <listitem> |
---|
516 | <para> |
---|
517 | Hibernate user documentation: <ulink |
---|
518 | url="http://www.hibernate.org/hib_docs/reference/en/html/persistent-classes.html#persistent-classes-equalshashcode">4.3. Implementing equals() and hashCode()</ulink> |
---|
519 | </para> |
---|
520 | </listitem> |
---|
521 | </itemizedlist> |
---|
522 | </sect3> |
---|
523 | |
---|
524 | <sect3 id="core_ref.rules.datalayer.nofinal"> |
---|
525 | <title>No final methods</title> |
---|
526 | <para> |
---|
527 | No methods should be tagged with the <constant>final</constant> keyword. This is a |
---|
528 | requirement to be able to use the proxy feature of Hibernate, which we need for |
---|
529 | performance reasons. |
---|
530 | </para> |
---|
531 | |
---|
532 | <para> |
---|
533 | See also: |
---|
534 | </para> |
---|
535 | <itemizedlist> |
---|
536 | <listitem> |
---|
537 | <para> |
---|
538 | Hibernate user documentation: <ulink |
---|
539 | url="http://www.hibernate.org/hib_docs/reference/en/html/persistent-classes.html#persistent-classes-pojo-final">4.1.3. Prefer non-final classes</ulink> |
---|
540 | </para> |
---|
541 | </listitem> |
---|
542 | <listitem> |
---|
543 | <para> |
---|
544 | Hibernate user documentation: <ulink |
---|
545 | url="http://www.hibernate.org/hib_docs/reference/en/html/performance.html#performance-fetching-proxies">19.1.3. Single-ended association proxies</ulink> |
---|
546 | </para> |
---|
547 | </listitem> |
---|
548 | </itemizedlist> |
---|
549 | |
---|
550 | </sect3> |
---|
551 | |
---|
552 | <sect3 id="core_ref.rules.datalayer.cache"> |
---|
553 | <title>Second-level cache</title> |
---|
554 | <para> |
---|
555 | To gain performance we use the second-level cache of Hibernate. It is a transparent |
---|
556 | feature that doesn't affect the code in any way. The second-level cache is configured |
---|
557 | in the <filename>hibernate.cfg.xml</filename> and <filename>ehcache.xml</filename> |
---|
558 | files and not in the individual class mapping files. BASE is shipped with a standard |
---|
559 | configuration, but different deployment scenarios may have to fine-tune the cache |
---|
560 | settings for that particular hardware/software setup. It is beyond the scope of |
---|
561 | this document to discuss this issue. |
---|
562 | </para> |
---|
563 | |
---|
564 | <para> |
---|
565 | The second-level cache is suitable for objects that are rarely modified but |
---|
566 | are often needed. For example, we do not expect the user information represented |
---|
567 | by the <classname docapi="net.sf.basedb.core.data">UserData</classname> object to change very often, but it is |
---|
568 | displayed all the time as the owner of various items. |
---|
569 | </para> |
---|
570 | |
---|
571 | <para> |
---|
572 | It is required that one thinks a bit of the usage of a class before coming |
---|
573 | up with a good caching strategy. We have to answer the following questions: |
---|
574 | </para> |
---|
575 | |
---|
576 | <orderedlist> |
---|
577 | <listitem> |
---|
578 | <para> |
---|
579 | Should objects of this class be cached at all? |
---|
580 | </para> |
---|
581 | </listitem> |
---|
582 | <listitem> |
---|
583 | <para> |
---|
584 | How long timeout should we use? |
---|
585 | </para> |
---|
586 | </listitem> |
---|
587 | <listitem> |
---|
588 | <para> |
---|
589 | How many objects should we keep in memory or on disk? |
---|
590 | </para> |
---|
591 | </listitem> |
---|
592 | </orderedlist> |
---|
593 | |
---|
594 | <para> |
---|
595 | The first question is the most important. Good candidates are classes with few |
---|
596 | objects that change rarely, but are read often. Also, objects which are linked |
---|
597 | to by many other objects are good candidates. The <classname docapi="net.sf.basedb.core.data">UserData</classname> |
---|
598 | class is an example which matches all three requirements. The <classname docapi="net.sf.basedb.core.data">LabelData</classname> |
---|
599 | class is an example which fulfils the first two. The <classname docapi="net.sf.basedb.core.data">BioMaterialEventData</classname> |
---|
600 | class is on the other hand a bad cache candidate, since it is not linked to any |
---|
601 | other object than a <classname docapi="net.sf.basedb.core.data">BioMaterialData</classname> object. |
---|
602 | </para> |
---|
603 | |
---|
604 | <para> |
---|
605 | The answer to the second question depends on how often an object is modified. |
---|
606 | For most objects this time is probably several days or months, but we would |
---|
607 | not gain much by keeping objects in the cache for so long. Suddenly, the |
---|
608 | information has changed and we won't risk that old information is kept that |
---|
609 | long. We have set the timeout to 1 hour for all classes so far, and we don't |
---|
610 | recommend a longer timeout. The only exception is for immutable objects, that |
---|
611 | cannot be changed at all, which may have an infinite timeout. |
---|
612 | </para> |
---|
613 | |
---|
614 | <para> |
---|
615 | The answer to the third question depends a lot on the hardware (available memory). |
---|
616 | With lots of memory we can afford to cache more objects. Caching to disk is not |
---|
617 | really necessary if the database is on the same machine as the web server, but |
---|
618 | if it is on another machine we have to consider the network delay to connect |
---|
619 | to the database versus the disk access time. The default configuration does not |
---|
620 | use disk cache. |
---|
621 | </para> |
---|
622 | |
---|
623 | <para> |
---|
624 | See also: |
---|
625 | </para> |
---|
626 | <itemizedlist> |
---|
627 | <listitem> |
---|
628 | <para> |
---|
629 | "Hibernate in action", chapter 5.3 "Caching theory and practice", page 175-194. |
---|
630 | </para> |
---|
631 | </listitem> |
---|
632 | <listitem> |
---|
633 | <para> |
---|
634 | Hibernate user documentation: <ulink |
---|
635 | url="http://www.hibernate.org/hib_docs/reference/en/html/performance.html#performance-cache">19.2. The Second Level Cache</ulink> |
---|
636 | </para> |
---|
637 | </listitem> |
---|
638 | </itemizedlist> |
---|
639 | |
---|
640 | </sect3> |
---|
641 | |
---|
642 | <sect3 id="core_ref.rules.datalayer.proxies"> |
---|
643 | <title>Proxies</title> |
---|
644 | |
---|
645 | <para> |
---|
646 | Proxies are also used to gain performance, and they may have some impact on |
---|
647 | the code. Proxies are created at runtime (by Hibernate) as a subclass of the |
---|
648 | actual class but are not populated with data until some method of the object |
---|
649 | is called. The data is loaded from the database the first time a method other |
---|
650 | than <methodname>getId()</methodname> is called. Thus, we can avoid loading |
---|
651 | data that is not needed at a particular time. |
---|
652 | </para> |
---|
653 | |
---|
654 | <para> |
---|
655 | There can be a problem with using the <code>instanceof</code> operator with proxies |
---|
656 | and the table-per-class-hierarchy mapping. For example, if we have the abstract |
---|
657 | class <classname>Animal</classname> and subclasses <classname>Cat</classname> |
---|
658 | and <classname>Dog</classname>. The proxy of an <classname>Animal</classname> is a |
---|
659 | runtime generated subclass of <classname>Animal</classname>, since we do not know if it |
---|
660 | is a <classname>Cat</classname> or <classname>Dog</classname>. So, |
---|
661 | <code>x instanceof Dog</code> and <code>x instanceof Cat</code> would both return |
---|
662 | false. If we hadn't used a proxy, at least one of them would always be true. |
---|
663 | </para> |
---|
664 | |
---|
665 | <para> |
---|
666 | Proxies are only used when a not-null object is linked with many-to-one or |
---|
667 | one-to-one from another object. If we ask for a specific object by id, or by a |
---|
668 | query, we will never get a proxy. Therefore, it only makes sense to enable |
---|
669 | proxies for classes that can be linked from other classes. One-to-one links on |
---|
670 | the primary key where null is allowed silently disables the proxy feature, |
---|
671 | since Hibernate doesn't know if there is an object or not without querying |
---|
672 | the database. |
---|
673 | </para> |
---|
674 | |
---|
675 | <bridgehead>Proxy vs. cache</bridgehead> |
---|
676 | <para> |
---|
677 | The goal of a proxy and the second-level cache are the same: to avoid hitting the |
---|
678 | database. It is perfectly possible to enable both proxies and the cache for a |
---|
679 | class. Then we would start with a proxy and as soon as a method is called Hibernate |
---|
680 | would look in the second-level cache. Only if it is not there it would be loaded |
---|
681 | from the database. But, do we really need a proxy in the first place? Well, I think |
---|
682 | it might be better to use only the cache or only proxies. But, this also makes it |
---|
683 | even more important that the cache is configured correctly so there is a high |
---|
684 | probability that the object is already in the cache. |
---|
685 | </para> |
---|
686 | |
---|
687 | <para> |
---|
688 | If a class has been configured to use the second-level cache, we recommend |
---|
689 | that proxies are disabled. For child objects in a parent-child relationship proxies |
---|
690 | should be disabled, since they have no other links to them than from the parent. |
---|
691 | If a class can be linked as many-to-one from several other classes it makes sense |
---|
692 | to enable proxies. If we have a long chain of many-to-one relations it may also make |
---|
693 | sense to enable proxies at some level, even if the second-level cache is used. |
---|
694 | In that case we only need to create one proxy instead of looking up several objects |
---|
695 | in the cache. Also, think about how a particular class most commonly will be used |
---|
696 | in a client application. For example, it is very common to display the name of the |
---|
697 | owner of an item, but we are probably not interested in displaying quota |
---|
698 | information for that user. So, it makes sense to put users in the second-level |
---|
699 | cache and use proxies for quota information. |
---|
700 | </para> |
---|
701 | |
---|
702 | <warning> |
---|
703 | <title>Batchable classes and stateless sessions</title> |
---|
704 | <para> |
---|
705 | Starting with Hibernate 3.1 there is a new stateless session feature. A |
---|
706 | stateless session has no first-level cache and doesn't use the second-level |
---|
707 | cache either. This means that if we load an item with a stateless session |
---|
708 | Hibernate will always traverse many-to-one and one-to-one associations and |
---|
709 | load those objects as well, unless they are configured to use proxies. |
---|
710 | </para> |
---|
711 | |
---|
712 | <para> |
---|
713 | Stateless sessions are used by batchable items (reporters, raw data and features) |
---|
714 | since they are many and we want to use as little memory as possible. Here it |
---|
715 | is required that proxies are enabled for all items that are linked from any of |
---|
716 | the batchable items, ie. <classname docapi="net.sf.basedb.core">RawBioAssay</classname>, |
---|
717 | <classname docapi="net.sf.basedb.core">ReporterType</classname>, |
---|
718 | <classname docapi="net.sf.basedb.core">ArrayDesignBlock</classname>, etc. |
---|
719 | If we don't do this Hibernate will generate multiple additional select |
---|
720 | statements for the same parent item which will affect performance |
---|
721 | in a bad way. |
---|
722 | </para> |
---|
723 | |
---|
724 | <para> |
---|
725 | On the other hand, the proxies created from a stateless session cannot later |
---|
726 | be initialised. We have to get the ID from the proxy and the load the object |
---|
727 | using the regular session. But this can also results in lots of additional select |
---|
728 | statements so if it is known before that we need some information it is recommended |
---|
729 | that a FETCH JOIN is used so that we get fully initialized objects instead of |
---|
730 | proxies to begin with. |
---|
731 | </para> |
---|
732 | </warning> |
---|
733 | |
---|
734 | <para> |
---|
735 | Here is a table which summarises different settings for the second-level cache, |
---|
736 | proxies, batch fetching and many-to-one links. Batch fetching and many-to-one links |
---|
737 | are discussed later in this document. |
---|
738 | </para> |
---|
739 | |
---|
740 | <para> |
---|
741 | First, decide if the second-level cache should be enabled or not. Then, if |
---|
742 | proxies should be enabled or not. The table then gives a reasonable setting for |
---|
743 | the batch size and many-to-one mappings. NOTE! The many-to-one mappings are |
---|
744 | the links from other classes to this one, not links from this class. |
---|
745 | </para> |
---|
746 | |
---|
747 | <para> |
---|
748 | The settings in this table are not absolute rules. In some cases there might |
---|
749 | be a good reason for another combination. Please, write a comment about why |
---|
750 | the recommendations were not followed. |
---|
751 | </para> |
---|
752 | |
---|
753 | <table id="core_ref.rules.datalayer.cacheproxysettings"> |
---|
754 | <title>Choosing cache and proxy settings</title> |
---|
755 | <tgroup cols="4"> |
---|
756 | <colspec colname="cache" /> |
---|
757 | <colspec colname="proxy" /> |
---|
758 | <colspec colname="batchsize" /> |
---|
759 | <colspec colname="outerjoin" /> |
---|
760 | |
---|
761 | <thead> |
---|
762 | <row> |
---|
763 | <entry>Global configuration</entry> |
---|
764 | <entry namest="proxy" nameend="batchsize">Class mapping</entry> |
---|
765 | <entry>Many-to-one mapping</entry> |
---|
766 | </row> |
---|
767 | <row> |
---|
768 | <entry>Cache</entry> |
---|
769 | <entry>Proxy</entry> |
---|
770 | <entry>Batch-size</entry> |
---|
771 | <entry>Outer-join</entry> |
---|
772 | </row> |
---|
773 | </thead> |
---|
774 | <tbody> |
---|
775 | <row> |
---|
776 | <entry>no</entry> |
---|
777 | <entry>no*</entry> |
---|
778 | <entry>yes</entry> |
---|
779 | <entry>true</entry> |
---|
780 | </row> |
---|
781 | <row> |
---|
782 | <entry>yes</entry> |
---|
783 | <entry>no*</entry> |
---|
784 | <entry>no</entry> |
---|
785 | <entry>false</entry> |
---|
786 | </row> |
---|
787 | <row> |
---|
788 | <entry>no</entry> |
---|
789 | <entry>yes</entry> |
---|
790 | <entry>yes</entry> |
---|
791 | <entry>false</entry> |
---|
792 | </row> |
---|
793 | <row> |
---|
794 | <entry>yes</entry> |
---|
795 | <entry>yes</entry> |
---|
796 | <entry>no</entry> |
---|
797 | <entry>false</entry> |
---|
798 | </row> |
---|
799 | </tbody> |
---|
800 | </tgroup> |
---|
801 | </table> |
---|
802 | |
---|
803 | <para> |
---|
804 | * = Do not use this setting for classes which are many-to-one linked from a batchable |
---|
805 | class. |
---|
806 | </para> |
---|
807 | |
---|
808 | <para> |
---|
809 | See also: |
---|
810 | </para> |
---|
811 | <itemizedlist> |
---|
812 | <listitem> |
---|
813 | <para> |
---|
814 | "Hibernate in action", chapter 4.4.6 "Selecting a fetching strategy in mappings", page 146-147 |
---|
815 | </para> |
---|
816 | </listitem> |
---|
817 | <listitem> |
---|
818 | <para> |
---|
819 | "Hibernate in action", chapter 6.4.1 "Polymorphic many-to-one associations", page 234-236 |
---|
820 | </para> |
---|
821 | </listitem> |
---|
822 | <listitem> |
---|
823 | <para> |
---|
824 | Hibernate user documentation: <ulink |
---|
825 | url="http://www.hibernate.org/hib_docs/reference/en/html/performance.html#performance-fetching-proxies">19.1.3. Single-ended association proxies</ulink> |
---|
826 | </para> |
---|
827 | </listitem> |
---|
828 | </itemizedlist> |
---|
829 | |
---|
830 | </sect3> |
---|
831 | |
---|
832 | <sect3 id="core_ref.rules.datalayer.hibernate"> |
---|
833 | <title>Hibernate mappings</title> |
---|
834 | |
---|
835 | <para> |
---|
836 | We use Javadoc tags to specify the database mapping needed by Hibernate. |
---|
837 | The tags are processed by XDoclet at build time which generates the XML-based |
---|
838 | Hibernate mapping files. |
---|
839 | </para> |
---|
840 | |
---|
841 | <note> |
---|
842 | <title>XDoclet doesn't support all mappings</title> |
---|
843 | <para> |
---|
844 | The XDoclet that we use was developed to generate mapping files for |
---|
845 | Hibernate 2.x. Since then, Hibernate has released several 3.x versions, |
---|
846 | and the mapping file structure has changed. Some changes can be handled by |
---|
847 | generating a corresponding 2.x mapping and then converting it to a 3.x |
---|
848 | mapping at build time using simple search-and-replace operations. |
---|
849 | One such case is to update the DTD reference to the 3.0 version instead of |
---|
850 | the 2.0 version. Other changes can't use this approach. Instead we have to |
---|
851 | provide extra mappings inside an XML files. This is also needed if we need |
---|
852 | to use some of the new 3.x features that has no 2.x counterpart. |
---|
853 | </para> |
---|
854 | </note> |
---|
855 | |
---|
856 | <simplesect id="core_ref.rules.datalayer.class"> |
---|
857 | <title>Class mapping</title> |
---|
858 | |
---|
859 | <programlisting language="java"> |
---|
860 | /** |
---|
861 | This class holds information about any data... |
---|
862 | @author Your name |
---|
863 | @version 2.0 |
---|
864 | @hibernate.class table="`Anys`" lazy="false" batch-size="10" |
---|
865 | @base.modified $Date: 2007-08-17 09:18:29 +0200 (Fri, 17 Aug 2007) $ |
---|
866 | */ |
---|
867 | public class AnyData |
---|
868 | extends CommonData |
---|
869 | { |
---|
870 | // Rest of class code... |
---|
871 | } |
---|
872 | </programlisting> |
---|
873 | |
---|
874 | <para> |
---|
875 | The class declaration must contain a <code>@hibernate.class</code> Javadoc entry |
---|
876 | where Hibernate can find the name of the table where items of this type are stored. |
---|
877 | The table name should generally be the same as the class name, without the ending |
---|
878 | <code>Data</code> and in a plural form. For example <classname docapi="net.sf.basedb.core.data">UserData</classname> |
---|
879 | --> <code>Users</code>. The back-ticks (`) around the table name tells Hibernate |
---|
880 | to enclose the name in whatever the actual database manager uses for such things |
---|
881 | (back-ticks in MySQL, quotes for an ANSI-compatible database). |
---|
882 | </para> |
---|
883 | |
---|
884 | <important> |
---|
885 | <title>Specify a value for the lazy attribute</title> |
---|
886 | <para> |
---|
887 | The lazy attribute enables/disables proxies for the class. Do not forget |
---|
888 | to specify this attribute since the default value is true. If proxies are |
---|
889 | enabled, it may also make sense to specify a batch-size attribute. Then |
---|
890 | Hibernate will load the specified number of items in each SELECT statement |
---|
891 | instead of loading them one by one. It may also make sense to specify a |
---|
892 | batch size when proxies are disabled, but then it would probably be even |
---|
893 | better to use eager fetching by setting <code>outer-join="true"</code> |
---|
894 | (see many-to-one mapping). |
---|
895 | </para> |
---|
896 | |
---|
897 | <para> |
---|
898 | Classes that are linked with a many-to-one association from a batchable |
---|
899 | class must specify <code>lazy="true"</code>. Otherwise the stateless session |
---|
900 | feature of Hibernate may result in a large number of SELECT:s for the same |
---|
901 | item, or even circular loops if two or more items references each other. |
---|
902 | </para> |
---|
903 | </important> |
---|
904 | |
---|
905 | <important> |
---|
906 | <title>Remember to enable the second-level cache</title> |
---|
907 | <para> |
---|
908 | Do not forget to configure settings for the second-level cache if this |
---|
909 | should be enabled. This is done in the <filename>hibernate.cfg.xml</filename> |
---|
910 | and <filename>ehcache.xml</filename>. |
---|
911 | </para> |
---|
912 | </important> |
---|
913 | |
---|
914 | <para> |
---|
915 | See also: |
---|
916 | </para> |
---|
917 | |
---|
918 | <itemizedlist> |
---|
919 | <listitem> |
---|
920 | <para> |
---|
921 | "Hibernate in action", chapter 3.3 "Defining the mapping metadata", page 75-87 |
---|
922 | </para> |
---|
923 | </listitem> |
---|
924 | <listitem> |
---|
925 | <para> |
---|
926 | Hibernate user documentation: <ulink |
---|
927 | url="http://www.hibernate.org/hib_docs/reference/en/html/mapping.html#mapping-declaration-class">5.1.3. class</ulink> |
---|
928 | </para> |
---|
929 | </listitem> |
---|
930 | </itemizedlist> |
---|
931 | |
---|
932 | </simplesect> |
---|
933 | |
---|
934 | <simplesect id="core_ref.rules.datalayer.property"> |
---|
935 | <title>Property mappings</title> |
---|
936 | |
---|
937 | <para> |
---|
938 | Properties such as strings, integers, dates, etc. are mapped with |
---|
939 | the <code>@hibernate.property</code> Javadoc tag. The main purpose |
---|
940 | is to define the database column name. The column names should |
---|
941 | generally be the same as the get/set method name without the get/set prefix, |
---|
942 | and with upper-case letters converted to lower-case and an underscore inserted. |
---|
943 | Examples: |
---|
944 | </para> |
---|
945 | |
---|
946 | <itemizedlist> |
---|
947 | <listitem> |
---|
948 | <para> |
---|
949 | <methodname>getAddress()</methodname> |
---|
950 | --> <code>column="`address`"</code> |
---|
951 | </para> |
---|
952 | </listitem> |
---|
953 | <listitem> |
---|
954 | <para> |
---|
955 | <methodname>getLoginComment()</methodname> |
---|
956 | --> <code>column="`login_comment`"</code> |
---|
957 | </para> |
---|
958 | </listitem> |
---|
959 | </itemizedlist> |
---|
960 | |
---|
961 | <para> |
---|
962 | The back-ticks (`) around the column name tells Hibernate to enclose |
---|
963 | the name in whatever the actual database manager uses for such things |
---|
964 | (back-ticks in MySQL, quotes for an ANSI-compatible database). |
---|
965 | </para> |
---|
966 | |
---|
967 | <bridgehead>String properties</bridgehead> |
---|
968 | |
---|
969 | <programlisting language="java"> |
---|
970 | public static int long MAX_STRINGPROPERTY_LENGTH = 255; |
---|
971 | private String stringProperty; |
---|
972 | /** |
---|
973 | Get the string property. |
---|
974 | @hibernate.property column="`string_property`" type="string" |
---|
975 | length="255" not-null="true" |
---|
976 | */ |
---|
977 | public String getStringProperty() |
---|
978 | { |
---|
979 | return stringProperty; |
---|
980 | } |
---|
981 | public void setStringProperty(String stringProperty) |
---|
982 | { |
---|
983 | this.stringProperty = stringProperty; |
---|
984 | } |
---|
985 | </programlisting> |
---|
986 | |
---|
987 | <para> |
---|
988 | Do not use a greater value than 255 for the length attribute. Some databases |
---|
989 | has that as the maximum length for character columns (ie. MySQL). If you need |
---|
990 | to store longer texts use <code>type="text"</code> instead. You can then skip |
---|
991 | the length attribute. Most databases will allow up to 65535 characters or more |
---|
992 | in a text field. Do not forget to specify the <code>not-null</code> attribute. |
---|
993 | </para> |
---|
994 | |
---|
995 | <para> |
---|
996 | You should also define a public constant <constant>MAX_STRINGPROPERTY_LENGTH</constant> |
---|
997 | containing the maximum allowed length of the string. |
---|
998 | </para> |
---|
999 | |
---|
1000 | <bridgehead>Numerical properties</bridgehead> |
---|
1001 | |
---|
1002 | <programlisting language="java"> |
---|
1003 | private int intProperty; |
---|
1004 | /** |
---|
1005 | Get the int property. |
---|
1006 | @hibernate.property column="`int_property`" type="int" not-null="true" |
---|
1007 | */ |
---|
1008 | public int getIntProperty() |
---|
1009 | { |
---|
1010 | return intProperty; |
---|
1011 | } |
---|
1012 | public void setIntProperty(int intProperty) |
---|
1013 | { |
---|
1014 | this.intProperty = intProperty; |
---|
1015 | } |
---|
1016 | </programlisting> |
---|
1017 | |
---|
1018 | <para> |
---|
1019 | It is also possible to use <classname>Integer</classname>, <classname>Long</classname> |
---|
1020 | or <classname>Float</classname> objects instead of <classname>int</classname>, |
---|
1021 | <classname>long</classname> and <classname>float</classname>. We have only used it |
---|
1022 | if null values have some meaning. |
---|
1023 | </para> |
---|
1024 | |
---|
1025 | <bridgehead>Boolean properties</bridgehead> |
---|
1026 | <programlisting language="java"> |
---|
1027 | private boolean booleanProperty; |
---|
1028 | /** |
---|
1029 | Get the boolean property. |
---|
1030 | @hibernate.property column="`boolean_property`" |
---|
1031 | type="boolean" not-null="true" |
---|
1032 | */ |
---|
1033 | public boolean isBooleanProperty() |
---|
1034 | { |
---|
1035 | return booleanProperty; |
---|
1036 | } |
---|
1037 | public void setBooleanProperty(boolean booleanProperty) |
---|
1038 | { |
---|
1039 | this.booleanProperty = booleanProperty; |
---|
1040 | } |
---|
1041 | </programlisting> |
---|
1042 | <para> |
---|
1043 | It is also possible to use a <classname>Boolean</classname> object instead of |
---|
1044 | <classname>boolean</classname>. It is only required if you absolutely need |
---|
1045 | null values to handle special cases. |
---|
1046 | </para> |
---|
1047 | |
---|
1048 | <bridgehead>Date values</bridgehead> |
---|
1049 | |
---|
1050 | <programlisting language="java"> |
---|
1051 | private Date dateProperty; |
---|
1052 | /** |
---|
1053 | Get the date property. Null is allowed. |
---|
1054 | @hibernate.property column="`date_property`" type="date" not-null="false" |
---|
1055 | */ |
---|
1056 | public Date getDateProperty() |
---|
1057 | { |
---|
1058 | return dateProperty; |
---|
1059 | } |
---|
1060 | public void setDateProperty(Date dateProperty) |
---|
1061 | { |
---|
1062 | this.dateProperty = dateProperty; |
---|
1063 | } |
---|
1064 | </programlisting> |
---|
1065 | |
---|
1066 | <para> |
---|
1067 | Hibernate defines several other date and time types. We have decided to use |
---|
1068 | the <code>type="date"</code> type when we are only interested in the date and |
---|
1069 | the <code>type="timestamp"</code> when we are interested in both the date |
---|
1070 | and time. |
---|
1071 | </para> |
---|
1072 | |
---|
1073 | <para> |
---|
1074 | See also: |
---|
1075 | </para> |
---|
1076 | |
---|
1077 | <itemizedlist> |
---|
1078 | <listitem> |
---|
1079 | <para> |
---|
1080 | "Hibernate in action", chapter 3.3.2 "Basic property and class mappings", page 78-84 |
---|
1081 | </para> |
---|
1082 | </listitem> |
---|
1083 | <listitem> |
---|
1084 | <para> |
---|
1085 | "Hibernate in action", chapter 6.1.1 "Built-in mapping types", page 198-200 |
---|
1086 | </para> |
---|
1087 | </listitem> |
---|
1088 | <listitem> |
---|
1089 | <para> |
---|
1090 | Hibernate user documentation: <ulink |
---|
1091 | url="http://www.hibernate.org/hib_docs/reference/en/html/mapping.html#mapping-declaration-property">5.1.9. property</ulink> |
---|
1092 | </para> |
---|
1093 | </listitem> |
---|
1094 | <listitem> |
---|
1095 | <para> |
---|
1096 | Hibernate user documentation: <ulink |
---|
1097 | url="http://www.hibernate.org/hib_docs/reference/en/html/mapping.html#mapping-types-basictypes">5.2.2. Basic value types</ulink> |
---|
1098 | </para> |
---|
1099 | </listitem> |
---|
1100 | </itemizedlist> |
---|
1101 | |
---|
1102 | </simplesect> |
---|
1103 | |
---|
1104 | <simplesect id="core_ref.rules.datalayer.manytoone"> |
---|
1105 | <title>Many-to-one mappings</title> |
---|
1106 | |
---|
1107 | <programlisting language="java"> |
---|
1108 | private OtherData other; |
---|
1109 | /** |
---|
1110 | Get the other object. |
---|
1111 | @hibernate.many-to-one column="`other_id`" not-null="true" outer-join="false" |
---|
1112 | */ |
---|
1113 | public OtherData getOther() |
---|
1114 | { |
---|
1115 | return other; |
---|
1116 | } |
---|
1117 | public void setOther(OtherData other) |
---|
1118 | { |
---|
1119 | this.other = other; |
---|
1120 | } |
---|
1121 | </programlisting> |
---|
1122 | |
---|
1123 | <para> |
---|
1124 | We create a many-to-one mapping with the <code>@hibernate.many-to-one</code> tag. |
---|
1125 | The most important attribute is the <code>column</code> attribute which specifies the name of |
---|
1126 | the database column to use for the id of the other item. The back-ticks (`) |
---|
1127 | around the column name tells Hibernate to enclose the name in whatever the |
---|
1128 | actual database manager uses for such things (back-ticks in MySQL, quotes for |
---|
1129 | an ANSI-compatible database). |
---|
1130 | </para> |
---|
1131 | |
---|
1132 | <para> |
---|
1133 | We also recommend that the <code>not-null</code> attribute is specified. Hibernate |
---|
1134 | will not check for null values, but it will generate table columns that allow |
---|
1135 | or disallow null values. See it as en extra safety feature while debugging. |
---|
1136 | It is also used to determine if Hibernate uses <code>LEFT JOIN</code> or |
---|
1137 | <code>INNER JOIN</code> in SQL statements. |
---|
1138 | </para> |
---|
1139 | |
---|
1140 | <para> |
---|
1141 | The <code>outer-join</code> attribute is important and affects how the |
---|
1142 | cache and proxies are used. It can take three values: <constant>auto</constant>, |
---|
1143 | <constant>true</constant> or <constant>false</constant>. If the value is |
---|
1144 | <constant>true</constant> Hibernate will always use a join to load the linked |
---|
1145 | object in a single select statement, overriding the cache and proxy settings. |
---|
1146 | This value should only be used if the class being linked has disabled both |
---|
1147 | proxies and the second-level cache, or if it is a link between a child |
---|
1148 | and parent in a parent-child relationship. A false value is best when |
---|
1149 | we expect the associated object to be in the second-level cache or proxying |
---|
1150 | is enabled. This is probably the most common case. The auto setting uses a |
---|
1151 | join if proxying is disabled otherwise it uses a proxy. Since we always |
---|
1152 | know if proxying is enabled or not, this setting is not very useful. See |
---|
1153 | <xref linkend="core_ref.rules.datalayer.cacheproxysettings" /> for the |
---|
1154 | recommended settings. |
---|
1155 | </para> |
---|
1156 | |
---|
1157 | <para> |
---|
1158 | See also: |
---|
1159 | </para> |
---|
1160 | |
---|
1161 | <itemizedlist> |
---|
1162 | <listitem> |
---|
1163 | <para> |
---|
1164 | "Hibernate in action", chapter 3.7 "Introducing associations", page 105-112 |
---|
1165 | </para> |
---|
1166 | </listitem> |
---|
1167 | <listitem> |
---|
1168 | <para> |
---|
1169 | "Hibernate in action", chapter 4.4.5-4.4.6 "Fetching strategies", page 143-151 |
---|
1170 | </para> |
---|
1171 | </listitem> |
---|
1172 | <listitem> |
---|
1173 | <para> |
---|
1174 | "Hibernate in action", chapter 6.4.1 "Polymorphic many-to-one associations", page 234-236 |
---|
1175 | </para> |
---|
1176 | </listitem> |
---|
1177 | <listitem> |
---|
1178 | <para> |
---|
1179 | Hibernate user documentation: <ulink |
---|
1180 | url="http://www.hibernate.org/hib_docs/reference/en/html/mapping.html#mapping-declaration-manytoone">5.1.10. many-to-one</ulink> |
---|
1181 | </para> |
---|
1182 | </listitem> |
---|
1183 | </itemizedlist> |
---|
1184 | |
---|
1185 | </simplesect> |
---|
1186 | |
---|
1187 | <simplesect id="core_ref.rules.datalayer.manytomany"> |
---|
1188 | <title>Many-to-many and one-to-many mappings</title> |
---|
1189 | |
---|
1190 | <para> |
---|
1191 | There are many variants of mapping many-to-many or one-to-many, and it is |
---|
1192 | not possible to give examples of all of them. In the code these mappings |
---|
1193 | are represented by <classname>Set</classname>:s, <classname>Map</classname>:s, |
---|
1194 | <classname>List</classname>:s, or some other collection object. The most |
---|
1195 | important thing to remember is that (in our application) the collections |
---|
1196 | are only used to maintain the links between objects. They are not used |
---|
1197 | for returning objects to client applications, as is the case with the |
---|
1198 | many-to-one mapping. |
---|
1199 | </para> |
---|
1200 | |
---|
1201 | <para> |
---|
1202 | For example, if we want to find all members of a group we do not use the |
---|
1203 | <code>GroupData.getUsers()</code> method, instead we will execute a database query |
---|
1204 | to retrieve them. The reason for this design is that the logged in user may |
---|
1205 | not have access to all users and we must add a permission checking filter |
---|
1206 | before returning the user objects to the client application. Using a query |
---|
1207 | will also allow client applications to specify sorting and filtering options |
---|
1208 | for the users that are returned. |
---|
1209 | </para> |
---|
1210 | |
---|
1211 | <programlisting language="java"> |
---|
1212 | // RoleData.java |
---|
1213 | private Set<UserData> users; |
---|
1214 | /** |
---|
1215 | Many-to-many from roles to users |
---|
1216 | @hibernate.set table="`UserRoles`" lazy="true" |
---|
1217 | @hibernate.collection-key column="`role_id`" |
---|
1218 | @hibernate.collection-many-to-many column="`user_id`" |
---|
1219 | class="net.sf.basedb.core.data.UserData" |
---|
1220 | */ |
---|
1221 | public Set<UserData> getUsers() |
---|
1222 | { |
---|
1223 | if (users == null) users = new HashSet<UserData>(); |
---|
1224 | return users; |
---|
1225 | } |
---|
1226 | void setUsers(Set<UserData> users) |
---|
1227 | { |
---|
1228 | this.users = users; |
---|
1229 | } |
---|
1230 | </programlisting> |
---|
1231 | |
---|
1232 | <para> |
---|
1233 | As you can see this mapping is a lot more complicated than what we have |
---|
1234 | seen before. The most important thing is the <code>lazy</code> attribute. |
---|
1235 | It tells Hibernate to delay the loading of the related objects until the set |
---|
1236 | is accessed. If the value is false or missing, Hibernate will load all objects |
---|
1237 | immediately. There is almost never a good reason to specify something other |
---|
1238 | than <code>lazy="true"</code>. |
---|
1239 | </para> |
---|
1240 | |
---|
1241 | <para> |
---|
1242 | Another important thing to remember is that the get method must always return |
---|
1243 | the same object that Hibernate passed to the set method. Otherwise, Hibernate |
---|
1244 | will not be able to detect changes made to the collection and as a result |
---|
1245 | will have to delete and then recreate all links. To ensure that the collection |
---|
1246 | object is not changed we have made the <methodname>setUsers()</methodname> method |
---|
1247 | package private, and the <methodname>getUsers()</methodname> will create a |
---|
1248 | new <classname>HashSet</classname> for us only if Hibernate didn't pass one |
---|
1249 | in the first place. |
---|
1250 | </para> |
---|
1251 | |
---|
1252 | <para> |
---|
1253 | Let's also have a look at the reverse mapping: |
---|
1254 | </para> |
---|
1255 | |
---|
1256 | <programlisting language="java"> |
---|
1257 | // UserData.java |
---|
1258 | private Set<RoleData> roles; |
---|
1259 | /** |
---|
1260 | Many-to-many from users to roles |
---|
1261 | @hibernate.set table="`UserRoles`" lazy="true" |
---|
1262 | @hibernate.collection-key column="`user_id`" |
---|
1263 | @hibernate.collection-many-to-many column="`role_id`" |
---|
1264 | class="net.sf.basedb.core.data.RoleData" |
---|
1265 | */ |
---|
1266 | Set<RoleData> getRoles() |
---|
1267 | { |
---|
1268 | return roles; |
---|
1269 | } |
---|
1270 | void setRoles(Set<RoleData> roles) |
---|
1271 | { |
---|
1272 | this.roles = roles; |
---|
1273 | } |
---|
1274 | </programlisting> |
---|
1275 | |
---|
1276 | <para> |
---|
1277 | The only real difference here is that both the setter and the getter methods |
---|
1278 | are package private. This is required because Hibernate will get confused if |
---|
1279 | we modify both ends. Thus, we are forced to always add/remove users to/from |
---|
1280 | the set in the <classname docapi="net.sf.basedb.core.data">GroupData</classname> object. The methods in the |
---|
1281 | <classname docapi="net.sf.basedb.core.data">RoleData</classname> class are never used by us. |
---|
1282 | Note that we do not have to check for null and create a new set since Hibernate |
---|
1283 | will handle null values as an empty set. |
---|
1284 | </para> |
---|
1285 | |
---|
1286 | <para> |
---|
1287 | So, why do we need the second collection at all? It is never accessed |
---|
1288 | except by Hibernate, and since it is lazy it will always be "empty". |
---|
1289 | The answer is that we want to use the relation in HQL statements. |
---|
1290 | For example: |
---|
1291 | </para> |
---|
1292 | |
---|
1293 | <programlisting language="sql"> |
---|
1294 | SELECT ... FROM GroupData grp WHERE grp.users ... |
---|
1295 | SELECT ... FROM UserData usr WHERE usr.groups ... |
---|
1296 | </programlisting> |
---|
1297 | |
---|
1298 | <para> |
---|
1299 | Without the inverse mapping, it would not have been possible to execute |
---|
1300 | the second HQL statement. The inverse mapping is also important in |
---|
1301 | parent-child relationships, where it is used to cascade delete the children |
---|
1302 | if a parent is deleted. |
---|
1303 | </para> |
---|
1304 | |
---|
1305 | <warning> |
---|
1306 | <title>Do not use the inverse="true" setting</title> |
---|
1307 | <para> |
---|
1308 | Hibernate defines an <code>inverse="true"</code> setting that can be used with |
---|
1309 | the <code>@hibernate.set</code> tag. If specified, Hibernate will ignore |
---|
1310 | changes made to that collection. However, there is one problem with specifying |
---|
1311 | this attribute. Hibernate doesn't delete entries in the association table, |
---|
1312 | leading to foreign key violations if we try to delete a user. The only |
---|
1313 | solutions are to skip the <code>inverse="true"</code> attribute or to |
---|
1314 | manually delete the object from all collections on the non-inverse end. |
---|
1315 | The first alternative is the most efficient since it only requires a |
---|
1316 | single SQL statement. The second alternative must first load all associated |
---|
1317 | objects and then issue a single delete statement for each association. |
---|
1318 | </para> |
---|
1319 | |
---|
1320 | <para> |
---|
1321 | In the "Hibernate in action" book they have a very different design |
---|
1322 | where they recommend that changes are made in both collections. We don't |
---|
1323 | have to do this since we are only interested in maintaining the links, |
---|
1324 | which is always done in one of the collections. |
---|
1325 | </para> |
---|
1326 | </warning> |
---|
1327 | |
---|
1328 | <bridgehead>Parent-child relationships</bridgehead> |
---|
1329 | |
---|
1330 | <para> |
---|
1331 | When one or more objects are tightly linked to some other object we talk |
---|
1332 | about a parent-child relationship. This kind of relationship becomes important |
---|
1333 | when we are about to delete a parent object. The children cannot exist |
---|
1334 | without the parent so they must also be deleted. Luckily, Hibernate can |
---|
1335 | do this for us if we specify a <code>cascade="delete"</code> option for the link. |
---|
1336 | This example is a one-to-many link between client and help texts. |
---|
1337 | </para> |
---|
1338 | |
---|
1339 | <programlisting language="java"> |
---|
1340 | // ClientData.java |
---|
1341 | private Set<HelpData> helpTexts; |
---|
1342 | /** |
---|
1343 | This is the inverse end. |
---|
1344 | @see HelpData#getClient() |
---|
1345 | @hibernate.set lazy="true" inverse="true" cascade="delete" |
---|
1346 | @hibernate.collection-key column="`client_id`" |
---|
1347 | @hibernate.collection-one-to-many class="net.sf.basedb.core.data.HelpData" |
---|
1348 | */ |
---|
1349 | Set<HelpData> getHelpTexts() |
---|
1350 | { |
---|
1351 | return helpTexts; |
---|
1352 | } |
---|
1353 | |
---|
1354 | void setHelpTexts(Set<HelpData> helpTexts) |
---|
1355 | { |
---|
1356 | this.helpTexts = helpTexts; |
---|
1357 | } |
---|
1358 | |
---|
1359 | // HelpData.java |
---|
1360 | private ClientData client; |
---|
1361 | /** |
---|
1362 | Get the client for this help text. |
---|
1363 | @hibernate.many-to-one column="`client_id`" not-null="true" |
---|
1364 | update="false" outer-join="false" unique-key="uniquehelp" |
---|
1365 | */ |
---|
1366 | public ClientData getClient() |
---|
1367 | { |
---|
1368 | return client; |
---|
1369 | } |
---|
1370 | public void setClient(ClientData client) |
---|
1371 | { |
---|
1372 | this.client = client; |
---|
1373 | } |
---|
1374 | </programlisting> |
---|
1375 | |
---|
1376 | <para> |
---|
1377 | This show both sides of the one-to-many mapping between parent and children. |
---|
1378 | As you can see the <code>@hibernate.set</code> doesn't specify a table, |
---|
1379 | since it is given by the <code>class</code> attribute of the |
---|
1380 | <code>@hibernate.collection-one-to-many</code> tag. |
---|
1381 | </para> |
---|
1382 | |
---|
1383 | <para> |
---|
1384 | In a one-to-many mapping, it is always the "one" side that handles the |
---|
1385 | link so the "many" side should always be mapped with <code>inverse="true"</code>. |
---|
1386 | </para> |
---|
1387 | |
---|
1388 | <bridgehead>Maps</bridgehead> |
---|
1389 | |
---|
1390 | <para> |
---|
1391 | Another type of many-to-many mapping uses a <interfacename>Map</interfacename> |
---|
1392 | for the collection. This kind of mapping is needed when the association between |
---|
1393 | two objects needs additional data to be kept as part of the association. |
---|
1394 | For example, the permission (stored as an integer value) given to users that |
---|
1395 | are members of a project. Note that you should use a <interfacename>Set</interfacename> |
---|
1396 | for mapping the inverse end. |
---|
1397 | </para> |
---|
1398 | |
---|
1399 | <programlisting language="java"> |
---|
1400 | // ProjectData.java |
---|
1401 | private Map<UserData, Integer> users; |
---|
1402 | /** |
---|
1403 | Many-to-many mapping between projects and users including permission values. |
---|
1404 | @hibernate.map table="`UserProjects`" lazy="true" |
---|
1405 | @hibernate.collection-key column="`project_id`" |
---|
1406 | @hibernate.index-many-to-many column="`user_id`" |
---|
1407 | class="net.sf.basedb.core.data.UserData" |
---|
1408 | @hibernate.collection-element column="`permission`" type="int" not-null="true" |
---|
1409 | */ |
---|
1410 | public Map<UserData, Integer> getUsers() |
---|
1411 | { |
---|
1412 | if (users == null) users = new HashMap<UserData, Integer>(); |
---|
1413 | return users; |
---|
1414 | } |
---|
1415 | void setUsers(Map<UserData, Integer> users) |
---|
1416 | { |
---|
1417 | this.users = users; |
---|
1418 | } |
---|
1419 | |
---|
1420 | // UserData.java |
---|
1421 | private Set<ProjectData> projects; |
---|
1422 | /** |
---|
1423 | This is the inverse end. |
---|
1424 | @see ProjectData#getUsers() |
---|
1425 | @hibernate.set table="`UserProjects`" lazy="true" |
---|
1426 | @hibernate.collection-key column="`user_id`" |
---|
1427 | @hibernate.collection-many-to-many column="`project_id`" |
---|
1428 | class="net.sf.basedb.core.data.ProjectData" |
---|
1429 | */ |
---|
1430 | Set<ProjectData> getProjects() |
---|
1431 | { |
---|
1432 | return projects; |
---|
1433 | } |
---|
1434 | void setProjects(Set<ProjectData> projects) |
---|
1435 | { |
---|
1436 | this.projects = projects; |
---|
1437 | } |
---|
1438 | </programlisting> |
---|
1439 | |
---|
1440 | <para> |
---|
1441 | See also: |
---|
1442 | </para> |
---|
1443 | |
---|
1444 | <itemizedlist> |
---|
1445 | <listitem> |
---|
1446 | <para> |
---|
1447 | "Hibernate in action", chapter 3.7 "Introducing associations", page 105-112 |
---|
1448 | </para> |
---|
1449 | </listitem> |
---|
1450 | <listitem> |
---|
1451 | <para> |
---|
1452 | "Hibernate in action", chapter 6.2 "Mapping collections of value types", page 211-220 |
---|
1453 | </para> |
---|
1454 | </listitem> |
---|
1455 | <listitem> |
---|
1456 | <para> |
---|
1457 | "Hibernate in action", chapter 6.3.2 "Many-to-many associations", page 225-233 |
---|
1458 | </para> |
---|
1459 | </listitem> |
---|
1460 | <listitem> |
---|
1461 | <para> |
---|
1462 | Hibernate user documentation: <ulink |
---|
1463 | url="http://www.hibernate.org/hib_docs/reference/en/html/collections.html">Chapter 6. Collection Mapping</ulink> |
---|
1464 | </para> |
---|
1465 | </listitem> |
---|
1466 | <listitem> |
---|
1467 | <para> |
---|
1468 | Hibernate user documentation: <ulink |
---|
1469 | url="http://www.hibernate.org/hib_docs/reference/en/html/example-parentchild.html">Chapter 21. Example: Parent/Child</ulink> |
---|
1470 | </para> |
---|
1471 | </listitem> |
---|
1472 | </itemizedlist> |
---|
1473 | |
---|
1474 | |
---|
1475 | </simplesect> |
---|
1476 | |
---|
1477 | <simplesect id="core_ref.rules.datalayer.onetoone"> |
---|
1478 | <title>One-to-one mappings</title> |
---|
1479 | |
---|
1480 | <para> |
---|
1481 | A one-to-one mapping can come in two different forms, depending on if both |
---|
1482 | objects should have the same id or not. We start with the case were the objects |
---|
1483 | can have different id:s and the link is done with an extra column in one of |
---|
1484 | the tables. The example is from the mapping between hybridizations and |
---|
1485 | arrayslides. |
---|
1486 | </para> |
---|
1487 | |
---|
1488 | <programlisting language="java"> |
---|
1489 | // HybridizationData.java |
---|
1490 | private ArraySlideData arrayslide; |
---|
1491 | /** |
---|
1492 | Get the array slide |
---|
1493 | @hibernate.many-to-one column="`arrayslide_id`" not-null="false" unique="true" |
---|
1494 | */ |
---|
1495 | public ArraySlideData getArraySlide() |
---|
1496 | { |
---|
1497 | return arrayslide; |
---|
1498 | } |
---|
1499 | public void setArraySlide(ArraySlideData arrayslide) |
---|
1500 | { |
---|
1501 | arrayslide.setHybridization(this); |
---|
1502 | this.arrayslide = arrayslide; |
---|
1503 | } |
---|
1504 | |
---|
1505 | // ArraySlideData.java |
---|
1506 | private HybridizationData hybridization; |
---|
1507 | /** |
---|
1508 | Get the hybridization |
---|
1509 | @hibernate.one-to-one property-ref="arraySlide" |
---|
1510 | */ |
---|
1511 | public HybridizationData getHybridization() |
---|
1512 | { |
---|
1513 | return hybridization; |
---|
1514 | } |
---|
1515 | void setHybridization(HybridizationData hybridization) |
---|
1516 | { |
---|
1517 | this.hybridization = hybridization; |
---|
1518 | } |
---|
1519 | </programlisting> |
---|
1520 | |
---|
1521 | <para> |
---|
1522 | As you can see, we use the many-to-one mapping on with a <code>unique="true"</code> |
---|
1523 | option for the hybridization. This will force the database to only allow the |
---|
1524 | same array slide to be linked once. Also note that since, <code>not-null="false"</code>, |
---|
1525 | null values are allowed and it doesn't matter which end of the relation that |
---|
1526 | is inserted first into the database. |
---|
1527 | </para> |
---|
1528 | <para> |
---|
1529 | For the array slide end we use a <code>@hibernate.one-to-one</code> |
---|
1530 | mapping and specify the name of the property on the other end that we are |
---|
1531 | linking to. Also, note that the we can only change the link with the |
---|
1532 | <methodname>HybridizationData.setArraySlide()</methodname> method, and that |
---|
1533 | this method also updates the other end. |
---|
1534 | </para> |
---|
1535 | |
---|
1536 | <para> |
---|
1537 | The second form of a one-to-one mapping is used when both objects must |
---|
1538 | have the same id (primary key). The example is from the mapping between users |
---|
1539 | and passwords. |
---|
1540 | </para> |
---|
1541 | |
---|
1542 | <programlisting language="java"> |
---|
1543 | // UserData.java |
---|
1544 | /** |
---|
1545 | @hibernate.id column="`id`" generator-class="foreign" |
---|
1546 | @hibernate.generator-param name="property" value="password" |
---|
1547 | */ |
---|
1548 | public int getId() |
---|
1549 | { |
---|
1550 | return super.getId(); |
---|
1551 | } |
---|
1552 | private PasswordData password; |
---|
1553 | /** |
---|
1554 | Get the password. |
---|
1555 | @hibernate.one-to-one class="net.sf.basedb.core.data.PasswordData" |
---|
1556 | cascade="all" outer-join="false" constrained="true" |
---|
1557 | */ |
---|
1558 | public PasswordData getPassword() |
---|
1559 | { |
---|
1560 | if (password == null) |
---|
1561 | { |
---|
1562 | password = new PasswordData(); |
---|
1563 | password.setUser(this); |
---|
1564 | } |
---|
1565 | return password; |
---|
1566 | } |
---|
1567 | void setPassword(PasswordData user) |
---|
1568 | { |
---|
1569 | this.password = password; |
---|
1570 | } |
---|
1571 | |
---|
1572 | // PasswordData.java |
---|
1573 | private UserData user; |
---|
1574 | /** |
---|
1575 | Get the user. |
---|
1576 | @hibernate.one-to-one class="net.sf.basedb.core.data.UserData" |
---|
1577 | */ |
---|
1578 | public UserData getUser() |
---|
1579 | { |
---|
1580 | return user; |
---|
1581 | } |
---|
1582 | void setUser(UserData user) |
---|
1583 | { |
---|
1584 | this.user = user; |
---|
1585 | } |
---|
1586 | </programlisting> |
---|
1587 | |
---|
1588 | <para> |
---|
1589 | In this case, we use the <code>@hibernate.one-to-one</code> mapping |
---|
1590 | in both classes. The <code>constrained="true"</code> tag in <classname docapi="net.sf.basedb.core.data">UserData</classname> |
---|
1591 | tells Hibernate to always insert the password first, and then the user. The makes it |
---|
1592 | possible to use the (auto-generated) id for the password as the id |
---|
1593 | for the user. This is controlled by the mapping for the <methodname>UserData.getId()</methodname> |
---|
1594 | method, which uses the <code>foreign</code> id generator. This generator will look |
---|
1595 | at the password property, ie. call <methodname>getPassword().getId()</methodname> |
---|
1596 | to find the id for the user. Also note the initialisation code and <code>cascade="all"</code> |
---|
1597 | tag in the <methodname>UserData.getPassword()</methodname> method. This is need |
---|
1598 | to avoid <classname>NullPointerException</classname>:s and to make sure everything |
---|
1599 | is created and deleted properly. |
---|
1600 | </para> |
---|
1601 | |
---|
1602 | <para> |
---|
1603 | See also: |
---|
1604 | </para> |
---|
1605 | |
---|
1606 | <itemizedlist> |
---|
1607 | <listitem> |
---|
1608 | <para> |
---|
1609 | "Hibernate in action", chapter 6.3.1 "One-to-one association", page 220-225 |
---|
1610 | </para> |
---|
1611 | </listitem> |
---|
1612 | <listitem> |
---|
1613 | <para> |
---|
1614 | Hibernate user documentation: <ulink |
---|
1615 | url="http://www.hibernate.org/hib_docs/reference/en/html/mapping.html#mapping-declaration-onetoone">5.1.11. one-to-one</ulink> |
---|
1616 | </para> |
---|
1617 | </listitem> |
---|
1618 | </itemizedlist> |
---|
1619 | |
---|
1620 | </simplesect> |
---|
1621 | |
---|
1622 | </sect3> |
---|
1623 | |
---|
1624 | <sect3 id="core_ref.rules.datalayer.documentation"> |
---|
1625 | <title>Documentation</title> |
---|
1626 | |
---|
1627 | <para> |
---|
1628 | The data layer code needs documentation. A simple approach is used for |
---|
1629 | Javadoc documentation |
---|
1630 | </para> |
---|
1631 | |
---|
1632 | <simplesect id="core_ref.rules.datalayer.documentation.class"> |
---|
1633 | <title>Class documentation</title> |
---|
1634 | <para> |
---|
1635 | The documentation for the class doesn't have to be very lengthy. A single |
---|
1636 | sentence is usually enough. Provide tags for the author, version, last modification date |
---|
1637 | and a reference to the corresponding class in the <package>net.sf.basedb.core</package> |
---|
1638 | package. |
---|
1639 | </para> |
---|
1640 | |
---|
1641 | <programlisting language="java"> |
---|
1642 | /** |
---|
1643 | This class holds information about any items. |
---|
1644 | |
---|
1645 | @author Your name |
---|
1646 | @version 2.0 |
---|
1647 | @see net.sf.basedb.core.AnyItem |
---|
1648 | @base.modified $Date: 2007-08-17 09:18:29 +0200 (Fri, 17 Aug 2007) $ |
---|
1649 | @hibernate.class table="`Anys`" lazy="false" |
---|
1650 | */ |
---|
1651 | public class AnyData |
---|
1652 | extends CommonData |
---|
1653 | { |
---|
1654 | ... |
---|
1655 | } |
---|
1656 | </programlisting> |
---|
1657 | |
---|
1658 | </simplesect> |
---|
1659 | |
---|
1660 | <simplesect id="core_ref.rules.datalayer.documentation.method"> |
---|
1661 | <title>Method documentation</title> |
---|
1662 | |
---|
1663 | <para> |
---|
1664 | Write a short one-sentence description for all public getter methods. You do not |
---|
1665 | have document the parameters or the setter methods, since it would just be a |
---|
1666 | repetition. Methods defined by interfaces are documented in the interface class. |
---|
1667 | You should not have to write any documentation for those methods. |
---|
1668 | </para> |
---|
1669 | |
---|
1670 | <para> |
---|
1671 | For the inverse end of an association, which has only package private methods, |
---|
1672 | write a notice about this and provide a link to to non-inverse end. |
---|
1673 | </para> |
---|
1674 | |
---|
1675 | <programlisting language="java"> |
---|
1676 | // UserData.java |
---|
1677 | private String address; |
---|
1678 | /** |
---|
1679 | Get the address for the user. |
---|
1680 | @hibernate.property column="`address`" type="string" length="255" |
---|
1681 | */ |
---|
1682 | public String getAddress() |
---|
1683 | { |
---|
1684 | return address; |
---|
1685 | } |
---|
1686 | public void setAddress(String address) |
---|
1687 | { |
---|
1688 | this.address = address; |
---|
1689 | } |
---|
1690 | |
---|
1691 | private Set<GroupData> groups; |
---|
1692 | /** |
---|
1693 | This is the inverse end. |
---|
1694 | @see GroupData#getUsers() |
---|
1695 | @hibernate.set table="`UserGroups`" lazy="true" inverse="true" |
---|
1696 | @hibernate.collection-key column="`user_id`" |
---|
1697 | @hibernate.collection-many-to-many column="`group_id`" |
---|
1698 | class="net.sf.basedb.core.data.GroupData" |
---|
1699 | */ |
---|
1700 | Set<GroupData> getGroups() |
---|
1701 | { |
---|
1702 | return groups; |
---|
1703 | } |
---|
1704 | void setGroups(Set<GroupData> groups) |
---|
1705 | { |
---|
1706 | this.groups = groups; |
---|
1707 | } |
---|
1708 | </programlisting> |
---|
1709 | |
---|
1710 | </simplesect> |
---|
1711 | |
---|
1712 | <simplesect id="core_ref.rules.datalayer.documentation.field"> |
---|
1713 | <title>Field documentation</title> |
---|
1714 | <para> |
---|
1715 | Write a short one-sentence description for <code>public static final</code> |
---|
1716 | fields. Private fields does not have to be documented. |
---|
1717 | </para> |
---|
1718 | |
---|
1719 | <programlisting language="java"> |
---|
1720 | /** |
---|
1721 | The maximum length of the name of an item that can be |
---|
1722 | stored in the database. |
---|
1723 | @see #setName(String) |
---|
1724 | */ |
---|
1725 | public static int MAX_NAME_LENGTH = 255; |
---|
1726 | </programlisting> |
---|
1727 | |
---|
1728 | </simplesect> |
---|
1729 | |
---|
1730 | <simplesect id="core_ref.rules.datalayer.documentation.uml"> |
---|
1731 | <title>UML diagram</title> |
---|
1732 | |
---|
1733 | <para> |
---|
1734 | Groups of related classes should be included in an UML-like |
---|
1735 | diagram to show how they are connected and work together. |
---|
1736 | For example we group together users, groups, roles, etc. into an |
---|
1737 | authentication UML diagram. It is also possible that a single class |
---|
1738 | may appear in more than one diagram. For more information about |
---|
1739 | how to create UML diagrams see |
---|
1740 | <xref linkend="documentation.magicdraw" />. |
---|
1741 | </para> |
---|
1742 | </simplesect> |
---|
1743 | |
---|
1744 | |
---|
1745 | </sect3> |
---|
1746 | |
---|
1747 | </sect2> |
---|
1748 | <sect2 id="core_ref.rules.itemclass"> |
---|
1749 | <title>Item-class rules</title> |
---|
1750 | <para> |
---|
1751 | This documentation is only available in the old format. |
---|
1752 | See <ulink url="http://base.thep.lu.se/chrome/site/doc/historical/development/coding/item/index.html" |
---|
1753 | >http://base.thep.lu.se/chrome/site/doc/historical/development/coding/item/index.html</ulink> |
---|
1754 | </para> |
---|
1755 | </sect2> |
---|
1756 | <sect2 id="core_ref.rules.batchclass"> |
---|
1757 | <title>Batch-class rules</title> |
---|
1758 | <para> |
---|
1759 | TODO |
---|
1760 | </para> |
---|
1761 | </sect2> |
---|
1762 | <sect2 id="core_ref.rules.testclass"> |
---|
1763 | <title>Test-class rules</title> |
---|
1764 | <para> |
---|
1765 | TODO |
---|
1766 | </para> |
---|
1767 | </sect2> |
---|
1768 | </sect1> |
---|
1769 | |
---|
1770 | <sect1 id="core_ref.coreinternals"> |
---|
1771 | <title>Internals of the Core API</title> |
---|
1772 | <para> |
---|
1773 | This documentation is only available in the old format. |
---|
1774 | See <ulink url="http://base.thep.lu.se/chrome/site/doc/historical/development/overview/core/index.html" |
---|
1775 | >http://base.thep.lu.se/chrome/site/doc/historical/development/overview/core/index.html</ulink> |
---|
1776 | </para> |
---|
1777 | <sect2 id="core_ref.authentication"> |
---|
1778 | <title>Authentication and sessions</title> |
---|
1779 | <para> |
---|
1780 | This documentation is only available in the old format. |
---|
1781 | See <ulink url="http://base.thep.lu.se/chrome/site/doc/historical/development/overview/core/authentication.html" |
---|
1782 | >http://base.thep.lu.se/chrome/site/doc/historical/development/overview/core/authentication.html</ulink> |
---|
1783 | </para> |
---|
1784 | </sect2> |
---|
1785 | <sect2 id="core_ref.accesspermissions"> |
---|
1786 | <title>Access permissions</title> |
---|
1787 | <para> |
---|
1788 | This documentation is only available in the old format. |
---|
1789 | See <ulink url="http://base.thep.lu.se/chrome/site/doc/historical/development/overview/core/accesspermissions.html" |
---|
1790 | >http://base.thep.lu.se/chrome/site/doc/historical/development/overview/core/accesspermissions.html</ulink> |
---|
1791 | </para> |
---|
1792 | </sect2> |
---|
1793 | <sect2 id="core_ref.datavalidation"> |
---|
1794 | <title>Data validation</title> |
---|
1795 | <para> |
---|
1796 | TODO |
---|
1797 | </para> |
---|
1798 | </sect2> |
---|
1799 | <sect2 id="core_ref.transactions"> |
---|
1800 | <title>Transaction handling</title> |
---|
1801 | <para> |
---|
1802 | TODO |
---|
1803 | </para> |
---|
1804 | </sect2> |
---|
1805 | <sect2 id="core_ref.crwd"> |
---|
1806 | <title>Create/read/write/delete operations</title> |
---|
1807 | <para> |
---|
1808 | This documentation is only available in the old format. |
---|
1809 | See <ulink url="http://base.thep.lu.se/chrome/site/doc/historical/development/overview/core/itemhandling.html" |
---|
1810 | >http://base.thep.lu.se/chrome/site/doc/historical/development/overview/core/itemhandling.html</ulink> |
---|
1811 | </para> |
---|
1812 | </sect2> |
---|
1813 | <sect2 id="core_ref.batch"> |
---|
1814 | <title>Batch operations</title> |
---|
1815 | <para> |
---|
1816 | This documentation is only available in the old format. |
---|
1817 | See <ulink url="http://base.thep.lu.se/chrome/site/doc/historical/development/overview/core/batchprocessing.html" |
---|
1818 | >http://base.thep.lu.se/chrome/site/doc/historical/development/overview/core/batchprocessing.html</ulink> |
---|
1819 | </para> |
---|
1820 | </sect2> |
---|
1821 | <sect2 id="core_ref.quota"> |
---|
1822 | <title>Quota</title> |
---|
1823 | <para> |
---|
1824 | TODO |
---|
1825 | </para> |
---|
1826 | </sect2> |
---|
1827 | <sect2 id="core_ref.pluginexecution"> |
---|
1828 | <title>Plugin execution / job queue</title> |
---|
1829 | <para> |
---|
1830 | This documentation is only available in the old format. |
---|
1831 | See <ulink url="http://base.thep.lu.se/chrome/site/doc/historical/development/overview/core/plugins.html" |
---|
1832 | >http://base.thep.lu.se/chrome/site/doc/historical/development/overview/core/plugins.html</ulink> |
---|
1833 | </para> |
---|
1834 | </sect2> |
---|
1835 | </sect1> |
---|
1836 | |
---|
1837 | </chapter> |
---|