<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
	xmlns:media="http://search.yahoo.com/mrss/"
>

<channel>
	<title>I can MAKE IT &#187; Java</title>
	<atom:link href="http://www.icanmakeit.de/category/computer/java/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.icanmakeit.de</link>
	<description>Hightech &#38; Lowlife</description>
	<lastBuildDate>Mon, 01 Apr 2013 12:46:42 +0000</lastBuildDate>
	<language>de-DE</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
	<copyright>Copyright © Hendrik Busch 2010, lizensiert unter Creative Commons Attribution-NonCommercial-ShareAlike 3.0 http://creativecommons.org/licenses/by-nc-sa/3.0/de/</copyright>
	<managingEditor>info@icanmakeit.de (Hendrik Busch)</managingEditor>
	<webMaster>info@icanmakeit.de (Hendrik Busch)</webMaster>
	<category>podcast</category>
	<ttl>1440</ttl>
	<image>
		<url>http://www.icanmakeit.de/media/feed-icon-blue-150x150.png</url>
		<title>I can MAKE IT</title>
		<link>http://www.icanmakeit.de</link>
		<width>144</width>
		<height>144</height>
	</image>
	<itunes:subtitle></itunes:subtitle>
	<itunes:summary>Hightech &#38; Lowlife</itunes:summary>
	<itunes:keywords></itunes:keywords>
	<itunes:category text="Society &#38; Culture" />
	<itunes:author>Hendrik Busch</itunes:author>
	<itunes:owner>
		<itunes:name>Hendrik Busch</itunes:name>
		<itunes:email>info@icanmakeit.de</itunes:email>
	</itunes:owner>
	<itunes:block>yes</itunes:block>
	<itunes:explicit>no</itunes:explicit>
	<itunes:image href="http://www.icanmakeit.de/media/feed-icon-blue-300x300.png" />
		<item>
		<title>How to get rid of &#8220;Could not resolve a persistence unit corresponding to the persistence-context-ref-name &#8230;&#8221; exceptions</title>
		<link>http://www.icanmakeit.de/2008/10/01/could-not-resolve-a-persistence-unit-corresponding-to-the-persistence-context-ref-name/</link>
		<comments>http://www.icanmakeit.de/2008/10/01/could-not-resolve-a-persistence-unit-corresponding-to-the-persistence-context-ref-name/#comments</comments>
		<pubDate>Wed, 01 Oct 2008 15:14:40 +0000</pubDate>
		<dc:creator>Hendrik Busch</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[application server]]></category>
		<category><![CDATA[developing java web applications]]></category>
		<category><![CDATA[Entwicklung]]></category>
		<category><![CDATA[glassfish]]></category>
		<category><![CDATA[jpa]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[unit tests]]></category>

		<guid isPermaLink="false">http://www.icanmakeit.de/?p=234</guid>
		<description><![CDATA[One error that&#8217;s been bugging me since I started out using Spring together with JPA in a Glassfish Application Server was an exception such as the following: com.sun.enterprise.deployment.backend.IASDeploymentException: Could not resolve a persistence unit corresponding to the persistence-context-ref-name [de.icanmakeit.jpokerstats.jpa.dao.GameDao/_entityManager] in the scope of the module called [jpokerstats]. Please verify your application. The core problem here [...]]]></description>
				<content:encoded><![CDATA[<p>One error that&#8217;s been bugging me since I started out using Spring together with JPA in a Glassfish Application Server was an exception such as the following:</p>
<blockquote><p>com.sun.enterprise.deployment.backend.IASDeploymentException: Could not resolve a persistence unit corresponding to the persistence-context-ref-name [de.icanmakeit.jpokerstats.jpa.dao.GameDao/_entityManager] in the scope of the module called [jpokerstats]. Please verify your application.</p></blockquote>
<p>The core problem here is that the mentioned <code>GameDao</code> uses the <code>@PersistenceContext</code> annotation to obtain an EntityManager from the container (or in this case Spring) but there are multiple persistence units defined in the <code>persistence.xml</code> (for the live system, for testing, etc.) and the Glassfish is unable to decide to which of these persistence units the annotation should be mapped.</p>
<p>Since I wanted all of this to be managed by Spring, I didn&#8217;t bother this error and worked around it by downgrading the <code>web.xml</code> from 2.5 to 2.4 which disabled the J5EE dependency injection mechanism in Glassfish and let Spring do its job. Simply specifying @PersistenceContext(unitName=&#8221;myunit&#8221;) wasn&#8217;t an option, since the same classes use a different persistence unit during JUnit tests and on the live server and explicitly specifying a persistence unit name breaks the unit tests.</p>
<p>Since my &#8220;inner ITIL&#8221; requires a developing solution after finding a workaround, I searched around the net, read a couple of dozen forum posts and finally figured out a way to solve the problem (&#8230;which I could have done earlier if I only had read the complete J5EE specification&#8230; *yawn*).</p>
<p>When deploying a 2.5 web application to Glassfish, the server examines it for any dependency injection annotations such as <code>@PersistenceContext</code>. It generates a default name for any unnamed annotation by using the FQCN, appended with a slash and the name of the field/property that is the injection target. In the above example, this would result in the name <code>de.icanmakeit.jpokerstats.jpa.dao.GameDao/_entityManager</code>. The server then tries find a persistence unit reference for this name. If there is only one persistence unit defined, this one will be used, otherwise the deployment descriptors are checked for configuration hints.</p>
<p>So in order to solve this problem, we have to provided these configuration hints. Adding the following few lines to the web.xml solves the problem:</p>
<p></p><pre class="crayon-plain-tag">&lt;persistence-context-ref&gt;
    &lt;persistence-context-ref-name&gt;de.icanmakeit.jpokerstats.jpa.dao.GameDao/_entityManager&lt;/persistence-context-ref-name&gt;
    &lt;persistence-unit-name&gt;pokerstats_live&lt;/persistence-unit-name&gt;
&lt;/persistence-context-ref&gt;</pre><p></p>
<p>This approach has one major drawback: if your software contains more than one <code>@PersistenceContext</code> injection target, you have to define a <code>persistence-context-ref</code> for each occurrence. This situation can be avoided by naming each @PersistenceContext annotation and giving them all the same name (<strong>note:</strong> the correct attribute is <code>name</code>, not <code>unitName</code>):</p>
<p></p><pre class="crayon-plain-tag">@PersistenceContext(name=&quot;pokerstats&quot;)
private EntityManager _entityManager;</pre><p></p>
<p>All injection target with the same annotation name can now share a single <code>persistence-context-ref</code> (you can also use this name to look up the persistence unit using JNDI):</p>
<p></p><pre class="crayon-plain-tag">&lt;persistence-context-ref&gt;
    &lt;persistence-context-ref-name&gt;pokerstats&lt;/persistence-context-ref-name&gt;
    &lt;persistence-unit-name&gt;pokerstats_live&lt;/persistence-unit-name&gt;
&lt;/persistence-context-ref&gt;</pre><p></p>
<p><strong>Update:</strong> Below you&#8217;ll find the relevant parts of the Spring configuration file for the example above. It defines two beans, the first one makes the datasource, that has been configured in the Glassfish Server, available for use in Spring. The second one defines the entity manager factory that Spring will use to support the JPA context.</p>
<p></p><pre class="crayon-plain-tag">&lt;bean id=&quot;dataSource&quot; class=&quot;org.springframework.jndi.JndiObjectFactoryBean&quot;&gt;
    &lt;property name=&quot;jndiName&quot; value=&quot;jdbc/__pokerstats&quot;/&gt;
&lt;/bean&gt;

&lt;bean id=&quot;entityManagerFactory&quot; class=&quot;org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean&quot;&gt;
    &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;/&gt;
    &lt;property name=&quot;persistenceUnitName&quot; value=&quot;pokerstats_live&quot;/&gt;
    &lt;property name=&quot;loadTimeWeaver&quot;&gt;
        &lt;bean class=&quot;org.springframework.instrument.classloading.glassfish.GlassFishLoadTimeWeaver&quot;/&gt;
    &lt;/property&gt;
    &lt;property name=&quot;persistenceProvider&quot;&gt;
        &lt;bean class=&quot;oracle.toplink.essentials.PersistenceProvider&quot;/&gt;
    &lt;/property&gt;
&lt;/bean&gt;</pre><p></p>
<!-- Beginn von `social share privacy by smeagol.de´ --><div id="socialshareprivacy2"></div>
			<script type="text/javascript">
			(function($){
				var options = {"info_link":"http:\/\/heise.de\/-1333879","txt_help":"Wenn Sie diese Felder durch einen Klick aktivieren, werden Informationen an Facebook, Twitter oder Google in die USA \u00fcbertragen und unter Umst\u00e4nden auch dort gespeichert. N\u00e4heres erfahren Sie durch einen Klick auf das <em>i<\/em>.","settings_perma":"Dauerhaft aktivieren und Daten\u00fcber\u00adtragung zustimmen:","cookie_path":"\/","cookie_expire":"365","cookie_domain":"","css_path":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/socialshareprivacy.css","oben":"nein","overall":"ja","ausschluss_private":"nein","services":{"facebook":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_facebook.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Facebook senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_fb_off":"nicht mit Facebook verbunden","txt_fb_on":"mit Facebook verbunden","display_name":"Facebook","referrer_track":"","language":"de_DE"},"twitter":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_twitter.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Twitter senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_twitter_off":"nicht mit Twitter verbunden","txt_twitter_on":"mit Twitter verbunden","display_name":"Twitter","referrer_track":"","tweet_text":"How to get rid of \"Could not resolve a persistence unit corresponding to the persistence-context-ref-name ...\" exceptions "},"gplus":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_gplus.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Google+ senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_gplus_off":"nicht mit Google+ verbunden","txt_gplus_on":"mit Google+ verbunden","display_name":"Google+","referrer_track":"","language":"de"}},"uri":"http:\/\/www.icanmakeit.de\/2008\/10\/01\/could-not-resolve-a-persistence-unit-corresponding-to-the-persistence-context-ref-name\/"};
				options.cookie_domain = document.location.host;
				$(document).ready(function(){
					$('#socialshareprivacy2').socialSharePrivacy(options);
				});
			})(jQuery);
			</script>
		<!-- Ende von `social share privacy by smeagol.de´ -->
	Tags: <a href="http://www.icanmakeit.de/tag/application-server/" title="application server" rel="tag">application server</a>, <a href="http://www.icanmakeit.de/tag/developing-java-web-applications/" title="developing java web applications" rel="tag">developing java web applications</a>, <a href="http://www.icanmakeit.de/tag/entwicklung/" title="Entwicklung" rel="tag">Entwicklung</a>, <a href="http://www.icanmakeit.de/tag/glassfish/" title="glassfish" rel="tag">glassfish</a>, <a href="http://www.icanmakeit.de/tag/java/" title="Java" rel="tag">Java</a>, <a href="http://www.icanmakeit.de/tag/jpa/" title="jpa" rel="tag">jpa</a>, <a href="http://www.icanmakeit.de/tag/spring/" title="spring" rel="tag">spring</a>, <a href="http://www.icanmakeit.de/tag/unit-tests/" title="unit tests" rel="tag">unit tests</a><br />
]]></content:encoded>
			<wfw:commentRss>http://www.icanmakeit.de/2008/10/01/could-not-resolve-a-persistence-unit-corresponding-to-the-persistence-context-ref-name/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Developing lightweight Java web applications &#8211; Part 4: Testing the persistence using Spring</title>
		<link>http://www.icanmakeit.de/2008/02/28/developing-lightweight-java-web-applications-part-4-testing-the-persistence-using-spring/</link>
		<comments>http://www.icanmakeit.de/2008/02/28/developing-lightweight-java-web-applications-part-4-testing-the-persistence-using-spring/#comments</comments>
		<pubDate>Thu, 28 Feb 2008 22:36:10 +0000</pubDate>
		<dc:creator>Hendrik Busch</dc:creator>
				<category><![CDATA[Entwicklung]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[developing java web applications]]></category>
		<category><![CDATA[facelets]]></category>
		<category><![CDATA[jpa]]></category>
		<category><![CDATA[jsf]]></category>
		<category><![CDATA[shale]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.icanmakeit.de/2008/02/28/developing-lightweight-java-web-applications-part-4-testing-the-persistence-using-spring/</guid>
		<description><![CDATA[Update 2012-05-28: I have deleted the project over at Google Code since I never finished it and it is outdated by now. Please refer to other, more state-of-the-art tutorials regarding building web applications with Java. In addition, comments are also closed on these posts. Now that all persistence classes are ready, we are going to [...]]]></description>
				<content:encoded><![CDATA[<p><em><strong>Update 2012-05-28:</strong> I have deleted the project over at Google Code since I never finished it and it is outdated by now. Please refer to other, more state-of-the-art tutorials regarding building web applications with Java. In addition, comments are also closed on these posts.</em></p>
<p>Now that all persistence classes are ready, we are going to see if they work the way we want. As we are going to use Spring in the web application, we can also use it for the tests as well, because the Spring IoC container is suitable for standalone applications and provides easy to configure testing facilities.</p>
<p>(Some people have asked me to use the more&#8230; function in these articles, so I will try that here.)</p>
<h3>Getting the sources</h3>
<p>As always, you can either go the <a title="JPokerStats project website" href="http://code.google.com/p/jpokerstats/">project website</a> or checkout the associated tag from the source control system:</p>
<blockquote><p><code>svn checkout http://jpokerstats.googlecode.com/svn/tags/tutorial_part_04</code></p></blockquote>
<h3>Changes that became necessary</h3>
<p>While writing this part of the tutorial, I had to change some of the classes to work the way I wanted. The most important change is switching the temporal type <code>startTime</code> and <code>endTime</code> in the Game class from <code>TemporalType.TIME</code> to <code>TemporalType.TIMESTAMP</code>. The reason behind this is the fact, that times saved to the database and loaded again are no longer equal (in terms of <code>equals()</code>) to the times before the save. The times contained are identical, but something else isn&#8217;t, so I had to switch as timestamps compare correctly.</p>
<p>As I am also learning while writing this, it may happen from time to time that I need to revise the code from a previous part of the tutorial. I will inform you of these changes as I do now.</p>
<h3>Configuring Spring</h3>
<p>We need a configuration file to configure Spring persistence, so we&#8217;ll create one for testing purposes. Don&#8217;t be discouraged by all the declarations in there, I&#8217;m going to explain them later on.</p>
<p><span id="more-173"></span></p>
<p></p><pre class="crayon-plain-tag">&lt;!--?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?--&gt;
xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
xmlns:tx=&quot;http://www.springframework.org/schema/tx&quot;
xmlns:p=&quot;http://www.springframework.org/schema/p&quot;
xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd&quot;&amp;gt;

&amp;nbsp;

&amp;nbsp;

&amp;nbsp;

&amp;nbsp;

&amp;nbsp;

FINE org.hsqldb.jdbcDriver jdbc:hsqldb:mem:jpokerstats_test sa oracle.toplink.essentials.platform.database.HSQLPlatform drop-and-create-tables

&amp;nbsp;</pre><p></p>
<p>You can read about all this configuration options in the excellent <a title="The Spring Reference Handbook" href="http://www.springframework.org/documentation">Spring reference handbook</a>. It might actually help you to understand what happens here. For starters, I recommend reading <a title="Spring Reference Handbook Chapter 3" href="http://static.springframework.org/spring/docs/2.5.x/reference/beans.html">chapter 3</a> and <a title="Spring Reference Handbook Chapter 12" href="http://static.springframework.org/spring/docs/2.5.x/reference/orm.html">chapter 12</a>.</p>
<p>First, the obvious part: I have declared a bean called <code>entityManagerFactory</code> and this will tell Spring how to configure itself to work with our persistent classes. This is also where we configure the database settings, although these are specific to the JPA implementation we are going to use. These options are simply passed through to Toplink.</p>
<p>Next I have defined a bean called <code>transactionManager</code>. Spring does not only manage our database connections, it also manages our transactions so that we don&#8217;t need to handle them ourselves or let the container handle them via JTA as not all containers provide container managed transactions. The Hint <code>&lt;tx:annotation-driven/&gt;</code> tells Spring that we will signal the use of transactions via annotations. This feature is pretty cool, you&#8217;ll see more of it in a later part.</p>
<p>The remaining two unnamed beans enable two other nice features. <code>PersistenceAnnotationBeanPostProcessor</code> is responsible for resolving any <code>@PersistenceContext</code> or <code>@PersistenceUnit</code> annotations within our application and inject an <code>EntityManager</code> or <code>EntityManagerFactory</code> instance, just like an EJB 3 container would do. The other class, <code>PersistenceExceptionTranslationPostProcessor</code>, will wrap any JPA specific exception with a corresponding Spring exception. As you may have read already, Spring supports many ORM providers, not just JPA. By wrapping the exceptions, your application only has to handle Spring exceptions while the underlying persistence layer can be freely exchanged.</p>
<h3>Writing the test itself</h3>
<p>The tests will use the Spring testing extension, a neat functionality that makes testing quite easy. I created a test case named <code>SpringJpaTest</code> that extends <code>AbstractJPATests</code>. This will cause Spring to initialize itself when the test is run and it provides a testing framework for JPA of which this test isn&#8217;t going to use much. To use all features provided by this superclass, we would have to configure a real persistence container with a DataSource and some load-time weaving. Sounds ugly? It is, at least for now, so we&#8217;ll stick to the simple things. The @ContextConfiguration tell Spring to load the configuration for this test class from a file in the same package named just like the class but suffixed the <code>-context.xml</code>. That&#8217;s why our configuration file is named <code>SpringJpaTest-context.xml</code>. To let Spring do it&#8217;s thing, we tell the class to run with the special <code>SpringJUnit4ClassRunner</code>.</p>
<p></p><pre class="crayon-plain-tag">@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class SpringJpaTest extends AbstractJpaTests
{
@PersistenceContext
private EntityManager _manager;

// other stuff
// ...

}</pre><p></p>
<p>The _manager field will be injected by Spring with an EntityManager just before the test is run. Each test method runs within it&#8217;s own transaction and the changed made to the database are rolled back when the method finishes. The method annotated with @Beforewill be called by JUnit once before each test method is run and we will use this mechanism to prepare the data to work with. Spring makes sure that the @Before-method runs in the same transaction as the actual test method.</p>
<p>I&#8217;m using HSQLDB for testing as well for the live application as it has a small footprint and is easy to configure. Toplink will create the necessary tables for us because we configured the <code>toplink.ddl-generation</code> option to <code>drop-and-create-tables</code>. This provides us with a clean database for each test cycle.</p>
<h4>Pitfalls</h4>
<p>The preparation of the data is pretty straightforward and contains no magic whatsoever. But there are some pitfalls I need to show you. German speaking readers might remember this <a title="German article about bi-directional relationships in JPA" href="http://www.icanmakeit.de/2006/11/14/jpa-bidirektionale-onetomanymanytoone-beziehungen/">article about bi-directional relationships in JPA</a>. We face the same problem here: a game references 0 or more game results and each of this game results references the game. For this to work and foremost for this to be saved to the database, we need to make both of these assignments (only an excerpt of the code is shown):</p>
<p></p><pre class="crayon-plain-tag">_game = new Game();
final GameResult result1 = new GameResult();

// assignment result -&amp;gt; game
result1.setGame(_game);

// assignment game -&amp;gt; result
_game.addResult(result1);</pre><p></p>
<p>I&#8217;ve seen people struggle with this and finally, they came up with the great idea that when one of this assignments is made, the other assignment is triggered and made automatically. So they fitted both method (in this case <code>setGame()</code> and <code>addResult()</code>) with some clever code like this:</p>
<p></p><pre class="crayon-plain-tag">public void setGame(final Game game)
{
if (null != game)
game.addResult(this);
_game = game;
}

public void addResult(final GameResult result)
{
if (null != result)
result.setGame(this);
_results.add(result)
}</pre><p></p>
<p>As you may easily see, this code will result in an infinite loop. I&#8217;ve tried to devise some collisionfree adaptation of this, but there is no clean way to do this within the entity classes themselves. In later development steps, we will use helper classes to do this work for us.</p>
<p>The next problem I came across was Toplink&#8217;s caching mechanism. The test works as follows: it creates some data objects, saves them to the database, loads them again and checks if they have been saved correctly. Unfortunately (at least for the testing) Toplink caches the objects currently attached to the EntityManager. If they get selected again (as they get in the test), the database itself is not queried and the results are delivered from the cache. To be able to test if really something was written to the database, we need to call <code>_manager.flush()</code> to make Toplink actually save to the database and then <code>_manager.clear()</code> to detach all entities saved so far.</p>
<p>The last pitfall: the test method itself has to be annotated with <code>@Transactional</code> or it will not participate in the same transaction used for <code>createTestData()</code>. As transactions are not commited by default, the database would be empty again by the time the test is run.</p>
<h3>What&#8217;s next?</h3>
<p>As this tutorial is about developing web applications, the next part finally deal with some web technology called Facelets, the templating framework for JSF. So stay tuned!</p>
<!-- Beginn von `social share privacy by smeagol.de´ --><div id="socialshareprivacy4"></div>
			<script type="text/javascript">
			(function($){
				var options = {"info_link":"http:\/\/heise.de\/-1333879","txt_help":"Wenn Sie diese Felder durch einen Klick aktivieren, werden Informationen an Facebook, Twitter oder Google in die USA \u00fcbertragen und unter Umst\u00e4nden auch dort gespeichert. N\u00e4heres erfahren Sie durch einen Klick auf das <em>i<\/em>.","settings_perma":"Dauerhaft aktivieren und Daten\u00fcber\u00adtragung zustimmen:","cookie_path":"\/","cookie_expire":"365","cookie_domain":"","css_path":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/socialshareprivacy.css","oben":"nein","overall":"ja","ausschluss_private":"nein","services":{"facebook":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_facebook.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Facebook senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_fb_off":"nicht mit Facebook verbunden","txt_fb_on":"mit Facebook verbunden","display_name":"Facebook","referrer_track":"","language":"de_DE"},"twitter":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_twitter.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Twitter senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_twitter_off":"nicht mit Twitter verbunden","txt_twitter_on":"mit Twitter verbunden","display_name":"Twitter","referrer_track":"","tweet_text":"Developing lightweight Java web applications - Part 4: Testing the persistence using Spring "},"gplus":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_gplus.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Google+ senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_gplus_off":"nicht mit Google+ verbunden","txt_gplus_on":"mit Google+ verbunden","display_name":"Google+","referrer_track":"","language":"de"}},"uri":"http:\/\/www.icanmakeit.de\/2008\/02\/28\/developing-lightweight-java-web-applications-part-4-testing-the-persistence-using-spring\/"};
				options.cookie_domain = document.location.host;
				$(document).ready(function(){
					$('#socialshareprivacy4').socialSharePrivacy(options);
				});
			})(jQuery);
			</script>
		<!-- Ende von `social share privacy by smeagol.de´ -->
	Tags: <a href="http://www.icanmakeit.de/tag/developing-java-web-applications/" title="developing java web applications" rel="tag">developing java web applications</a>, <a href="http://www.icanmakeit.de/tag/facelets/" title="facelets" rel="tag">facelets</a>, <a href="http://www.icanmakeit.de/tag/jpa/" title="jpa" rel="tag">jpa</a>, <a href="http://www.icanmakeit.de/tag/jsf/" title="jsf" rel="tag">jsf</a>, <a href="http://www.icanmakeit.de/tag/shale/" title="shale" rel="tag">shale</a>, <a href="http://www.icanmakeit.de/tag/spring/" title="spring" rel="tag">spring</a>, <a href="http://www.icanmakeit.de/tag/web/" title="web" rel="tag">web</a><br />
]]></content:encoded>
			<wfw:commentRss>http://www.icanmakeit.de/2008/02/28/developing-lightweight-java-web-applications-part-4-testing-the-persistence-using-spring/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Developing lightweight Java web applications &#8211; Part 3: Persistence</title>
		<link>http://www.icanmakeit.de/2008/02/26/developing-lightweight-java-web-applications-part-3-persistence/</link>
		<comments>http://www.icanmakeit.de/2008/02/26/developing-lightweight-java-web-applications-part-3-persistence/#comments</comments>
		<pubDate>Tue, 26 Feb 2008 12:20:37 +0000</pubDate>
		<dc:creator>Hendrik Busch</dc:creator>
				<category><![CDATA[Entwicklung]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[developing java web applications]]></category>
		<category><![CDATA[facelets]]></category>
		<category><![CDATA[jpa]]></category>
		<category><![CDATA[jsf]]></category>
		<category><![CDATA[shale]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.icanmakeit.de/2008/02/26/developing-lightweight-java-web-applications-part-3-persistence/</guid>
		<description><![CDATA[Update 2012-05-28: I have deleted the project over at Google Code since I never finished it and it is outdated by now. Please refer to other, more state-of-the-art tutorials regarding building web applications with Java. In addition, comments are also closed on these posts. In the last part of the tutorial, we spent some time [...]]]></description>
				<content:encoded><![CDATA[<p><em><strong>Update 2012-05-28:</strong> I have deleted the project over at Google Code since I never finished it and it is outdated by now. Please refer to other, more state-of-the-art tutorials regarding building web applications with Java. In addition, comments are also closed on these posts.</em></p>
<p>In the last part of the tutorial, we spent some time with the basic setup of the project and wrote our first classes that we&#8217;re going to use as JPA entities. You might recall the simple entity/relationship diagram from the last part that showed how these classes, that JPA will map to database tables for us, are related to each other. Let&#8217;s recap:</p>
<ul>
<li>A game can have many results</li>
<li>A result belongs to exactly one game and to exactly one player</li>
<li>A player can have many results</li>
</ul>
<p><span id="more-170"></span><br />
The <code>GameResult</code> class is what actually binds games and players together. The player that participated in a game can easily be determined by checking the results of the game, as each of these is associated with a player. This relationship can be used the other way round to check in which games a player participated. So now it&#8217;s time to tell JPA about the relationships.</p>
<h3>Getting the sources</h3>
<p>As always, you can either go the <a title="JPokerStats project website" href="http://code.google.com/p/jpokerstats/">project website</a> or checkout the associated tag from the source control system:</p>
<blockquote><p><code>svn checkout http://jpokerstats.googlecode.com/svn/tags/tutorial_part_03</code></p></blockquote>
<h3>Establishing relationships</h3>
<p>First, we add two new field together with getters and setters to the <code>GameResult</code> class. One of it holds an instance of <code>Game</code>, the other an instance of <code>Player</code>, so the the result can tell us to which game and to which player it belongs. In more technical terms, <code>GameResult</code> has a many-to-one relationship with <code>Game</code> and <code>Player</code>. The way JPA is designed, this forces us to declare <code>GameResult</code> the owner of the relationship. This can be done by annotating the appropriate getters with <code>@ManyToOne</code>. The target entity classes will be resolved by JPA automatically by evaluating the field type.</p>
<p></p><pre class="crayon-plain-tag">private Player _player;

private Game _game;

@ManyToOne
public Player getPlayer()
{
return _player;
}

public void setPlayer(final Player player)
{
_player = player;
}

@ManyToOne
public Game getGame()
{
return _game;
}

public void setGame(final Game game)
{
_game = game;
}</pre><p></p>
<p>As we have many-to-one relationships, both <code>Player</code> and <code>Game</code> must declare a collection of <code>GameResult</code> objects. In each of these classes, we introduce a field of type Set&lt;GameResult&gt; and add getters and setters for it. As you are free to name your methods as you like, we need to give JPA a hint on how to connect the relationships. So we annotate the getters with <code>@OneToMany</code> and specify the <code>mappedBy</code> attribute. This is a string that tells JPA which property on the related class is the counterpart for this relationship.</p>
<p></p><pre class="crayon-plain-tag">private Set_results;

@OneToMany(mappedBy=&quot;game&quot;)
public Set getResults()
{
return _results;
}

public void setResults(Setresults)
{
_results = results;
}</pre><p></p>
<h4>Why using the <code>mappedBy</code> attribute?</h4>
<p>Using the <code>mappedBy</code> attribute may seem unecessary at first glance but think about a situation where you have entities A and B. A is a person record and B a transaction record. B references two person records at once, e.g. in a buyer/seller situation. Now B suddenly has two fields of type A and JPA cannot determine, which belongs to which relationship. You can solve the situation by using the <code>mappedBy</code> attribute to make the relationships unique again.</p>
<h4>Why using sets instead of lists?</h4>
<p>Reading a tutorial like this I&#8217;d assume you know why, but let me tell you. There is only a single logical difference between a list and a set: a list has an order whereas a set has not. But this simple logical difference is a big technical one: append, insert and remove operations on list cause far more overhead than they do on sets, so sets are a lot faster. In this example, we also do not need the order of elements in the sets as we will re-sort them according to the users preferences in the web frontend later on. If you do not believe me, you could write you own test program. Although I have to say that the increase in performance by using sets instead of lists will be eaten up by the <em>&#8220;black magic&#8221;</em> that Spring, Facelets and all the others use to get the application working. I guess, everything comes at a price, but that&#8217;s fine with me. If it were otherwise, I would write everything myself like most C people do.</p>
<h3>Saving us trouble</h3>
<p>The are a lot of things to stumble or even fall over when working with JPA. In order to avoid mistakes right from the start, I need to explain some things and add some extra code.</p>
<p>First of all, we&#8217;ll not be working with whole sets of objects at once, so we add extra methods to work with single objects at a time:</p>
<p></p><pre class="crayon-plain-tag">public void addResult(final GameResult result)
{
_results.add(result);
}

public void removeResult(final GameResult result)
{
_results.remove(result);
}</pre><p></p>
<p>You might have noticed another problem. What if the set is not initialized? To avoid countless NPEs, we add a constructor to <code>Game</code> and <code>Player</code> and make sure the set gets initialized.</p>
<p></p><pre class="crayon-plain-tag">public Game()
{
_results = new HashSet();
}</pre><p></p>
<p>One big source of errors when working with JPA revolves around object identity. The question here is: Under what conditions are two objects treated as equal? Let&#8217;s assume you create 5 Player instances, set the names to John Doe and add them to a set. You end up with 5 John Does in a set. This is not what we want, we want only one John Doe in the set. This gets even worse if you load the same record from the database twice. Although it is the same record, the objects will no be treated as such. If one is already in a list and you ask Java: <em>&#8220;Hey, is there a John Doe in there?&#8221;</em> and use the other object, Java will respond: <em>&#8220;No!&#8221;</em>. This will get you into serious trouble and from my experience, 80% of all problems in the persistence layer of a project are caused by identity problems. <a title="Identity problem with No-Fly lists (Wikipedia)" href="http://en.wikipedia.org/wiki/No_Fly_List#False_positives">To fully understand the seriousness of identity problems, consider the US no-fly lists.</a></p>
<p>There is a book called Effective Java: Language Programming Guide. It is quite old now, but its contents remain valid even today. I recommend reading <a title="Google Books: Effective Java: Language Programming Guide" href="http://books.google.de/books?id=ZZOiqZQIbRMC&amp;printsec=frontcover#PPA36,M1">chapter 8: Always override hashCode() when you override equals()</a>, it describes a solution to our problem. For further reading please refer to the articles on equals() (<a title="Langer/Kreft: The Secrets of Equals Part 1" href="http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals.html">1</a>, <a title="Langer/Kreft: The Secrets of Equals Part 2" href="http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals-2.html">2</a>) by Angelika Langer and Klaus Kreft. Readers capable of reading German can read the articles in German: <a title="Angelika Langer: Objektvergleich" href="http://www.angelikalanger.com/Articles/JavaSpektrum/01.Equals-Part1/01.Equals1.html">1</a>, <a title="Angelika Langer: Objektvergleich Teil 2" href="http://www.angelikalanger.com/Articles/JavaSpektrum/02.Equals-Part2/02.Equals2.html">2</a>, <a title="Angelika Langer: HashCode-Berechnungen" href="http://www.angelikalanger.com/Articles/JavaSpektrum/03.HashCode/03.HashCode.html">3</a>.</p>
<p>After reading this, we&#8217;ll implement the <code>equals()</code> and <code>hashCode()</code> methods for all entity objects. It is important not to include the dataset id in the hash, as it is not part of the semantic content of a record. Most likely you IDE can generate those methods for you. Below is an example for the <code>Game</code> class:</p>
<p></p><pre class="crayon-plain-tag">@Override
public int hashCode()
{
final int offset = 11;
final int multiplier = 37;
int hashCode = offset;

// long values will be split into two integers
hashCode += (int)(_buyinChips&amp;gt;&amp;gt;&amp;gt;32) * multiplier;
hashCode += (int)(_buyinChips &amp;amp; 0xFFFFFFFF) * multiplier;
hashCode += (int)(_buyinCosts&amp;gt;&amp;gt;&amp;gt;32) * multiplier;
hashCode += (int)(_buyinCosts &amp;amp; 0xFFFFFFFF) * multiplier;
hashCode += (int)(_rebuyChips&amp;gt;&amp;gt;&amp;gt;32) * multiplier;
hashCode += (int)(_rebuyChips &amp;amp; 0xFFFFFFFF) * multiplier;
hashCode += (int)(_rebuyCosts&amp;gt;&amp;gt;&amp;gt;32) * multiplier;
hashCode += (int)(_rebuyCosts &amp;amp; 0xFFFFFFFF) * multiplier;

// reference values have to be null-tested
if (null != _date) hashCode += _date.hashCode() * multiplier;
if (null != _startTime) hashCode += _startTime.hashCode() * multiplier;
if (null != _endTime) hashCode += _endTime.hashCode() * multiplier;
if (null != _gameType) hashCode += _gameType.hashCode() * multiplier;

return hashCode;
}

@Override
public boolean equals(final Object other)
{
if (null == other || !(getClass().equals(other.getClass())))
return false;

final Game otherGame = (Game) other;

if (null == _date)
if (null != otherGame.getDate())
return false;

if (null == _endTime)
if (null != otherGame.getEndTime())
return false;

if (null == _startTime)
if (null != otherGame.getStartTime())
return false;

if (null == _gameType)
if (null != otherGame.getGameType())
return false;

// we start off assuming the objects are equal and use AND
// operations for the comparisons
boolean equals = true;
equals &amp;amp;= getBuyinChips() == otherGame.getBuyinChips();
equals &amp;amp;= getBuyinCosts() == otherGame.getBuyinCosts();
equals &amp;amp;= getRebuyChips() == otherGame.getRebuyChips();
equals &amp;amp;= getRebuyCosts() == otherGame.getRebuyCosts();

if (null != getDate())
equals &amp;amp;= getDate().equals(otherGame.getDate());

if (null != getStartTime())
equals &amp;amp;= getStartTime().equals(otherGame.getStartTime());

if (null != getEndTime())
equals &amp;amp;= getEndTime().equals(otherGame.getEndTime());

if (null != getGameType())
equals &amp;amp;= getGameType().equals(otherGame.getGameType());

return equals;
}</pre><p></p>
<p>I added some unit tests to make sure that these methods actually work and that the contract for <code>equals()</code> and <code>hashCode()</code> is not violated.</p>
<h3>What&#8217;s next?</h3>
<p>The next part of this tutorial will deal with even more testing. Now that <code>hashCode()</code> and <code>equals()</code> are implemented, it&#8217;s time to see if our model works when used in a real database. We will create some basic tests using <a title="The HSQLDB project website" href="http://www.hsqldb.org/">HSQLDB</a> and let Spring do the configuration work for us.</p>
<!-- Beginn von `social share privacy by smeagol.de´ --><div id="socialshareprivacy6"></div>
			<script type="text/javascript">
			(function($){
				var options = {"info_link":"http:\/\/heise.de\/-1333879","txt_help":"Wenn Sie diese Felder durch einen Klick aktivieren, werden Informationen an Facebook, Twitter oder Google in die USA \u00fcbertragen und unter Umst\u00e4nden auch dort gespeichert. N\u00e4heres erfahren Sie durch einen Klick auf das <em>i<\/em>.","settings_perma":"Dauerhaft aktivieren und Daten\u00fcber\u00adtragung zustimmen:","cookie_path":"\/","cookie_expire":"365","cookie_domain":"","css_path":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/socialshareprivacy.css","oben":"nein","overall":"ja","ausschluss_private":"nein","services":{"facebook":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_facebook.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Facebook senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_fb_off":"nicht mit Facebook verbunden","txt_fb_on":"mit Facebook verbunden","display_name":"Facebook","referrer_track":"","language":"de_DE"},"twitter":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_twitter.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Twitter senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_twitter_off":"nicht mit Twitter verbunden","txt_twitter_on":"mit Twitter verbunden","display_name":"Twitter","referrer_track":"","tweet_text":"Developing lightweight Java web applications - Part 3: Persistence "},"gplus":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_gplus.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Google+ senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_gplus_off":"nicht mit Google+ verbunden","txt_gplus_on":"mit Google+ verbunden","display_name":"Google+","referrer_track":"","language":"de"}},"uri":"http:\/\/www.icanmakeit.de\/2008\/02\/26\/developing-lightweight-java-web-applications-part-3-persistence\/"};
				options.cookie_domain = document.location.host;
				$(document).ready(function(){
					$('#socialshareprivacy6').socialSharePrivacy(options);
				});
			})(jQuery);
			</script>
		<!-- Ende von `social share privacy by smeagol.de´ -->
	Tags: <a href="http://www.icanmakeit.de/tag/developing-java-web-applications/" title="developing java web applications" rel="tag">developing java web applications</a>, <a href="http://www.icanmakeit.de/tag/facelets/" title="facelets" rel="tag">facelets</a>, <a href="http://www.icanmakeit.de/tag/jpa/" title="jpa" rel="tag">jpa</a>, <a href="http://www.icanmakeit.de/tag/jsf/" title="jsf" rel="tag">jsf</a>, <a href="http://www.icanmakeit.de/tag/shale/" title="shale" rel="tag">shale</a>, <a href="http://www.icanmakeit.de/tag/spring/" title="spring" rel="tag">spring</a>, <a href="http://www.icanmakeit.de/tag/web/" title="web" rel="tag">web</a><br />
]]></content:encoded>
			<wfw:commentRss>http://www.icanmakeit.de/2008/02/26/developing-lightweight-java-web-applications-part-3-persistence/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Developing lightweight Java web applications &#8211; Part 2: Getting started</title>
		<link>http://www.icanmakeit.de/2008/02/08/developing-lightweight-java-web-applications-part-2-getting-started/</link>
		<comments>http://www.icanmakeit.de/2008/02/08/developing-lightweight-java-web-applications-part-2-getting-started/#comments</comments>
		<pubDate>Fri, 08 Feb 2008 19:12:26 +0000</pubDate>
		<dc:creator>Hendrik Busch</dc:creator>
				<category><![CDATA[Entwicklung]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[developing java web applications]]></category>
		<category><![CDATA[facelets]]></category>
		<category><![CDATA[jpa]]></category>
		<category><![CDATA[jsf]]></category>
		<category><![CDATA[shale]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.icanmakeit.de/2008/02/08/developing-lightweight-java-web-applications-part-2-getting-started/</guid>
		<description><![CDATA[Update 2012-05-28: I have deleted the project over at Google Code since I never finished it and it is outdated by now. Please refer to other, more state-of-the-art tutorials regarding building web applications with Java. In addition, comments are also closed on these posts. Welcome to part two of my tutorial for developing lightweight Java [...]]]></description>
				<content:encoded><![CDATA[<p><em><strong>Update 2012-05-28:</strong> I have deleted the project over at Google Code since I never finished it and it is outdated by now. Please refer to other, more state-of-the-art tutorials regarding building web applications with Java. In addition, comments are also closed on these posts.</em></p>
<p>Welcome to part two of my tutorial for developing lightweight Java web applications! I meanwhile created an online code repository for the source files yo that you may refer to them more easily. You can either <a title="JPokerStats project website" href="http://code.google.com/p/jpokerstats/">visit the project site</a> or you can checkout the source files representing this part of the tutorial by using Subversion and calling the following command:</p><pre class="crayon-plain-tag">svn checkout http://jpokerstats.googlecode.com/svn/tags/tutorial_part_02</pre><p></p>
<p>I have always been the hands-on guy, and I&#8217;ve always disliked documentations and tutorials that explain aspect of something without a surrounding context So in this tutorial, I&#8217;m going to explain the aspects of lightweight Java web applications by using a complete example application that actually does something.</p>
<p><span id="more-168"></span></p>
<p>During this tutorial, I will often skip man explanations of the source code itself, pointing out only the interesting and challenging parts. To get the whole picture, <a title="JPokerStats source repository view" href="http://code.google.com/p/jpokerstats/source/browse">please refer to the source code repository</a>.</p>
<h3>Introducing JPokerStats</h3>
<p>The application that is going to be developed is simply called JPokerStats. I play a lot of poker and I use a self-written Ruby on Rails application to track the games I&#8217;ve played with my friends. Basically what I&#8217;m doing now is rewriting the software in Java.<br />
The database model behind JPokerStats is made up of three tables that will store all necessary data:</p>
<p><img src="http://www.icanmakeit.de/wp-content/uploads/2008/02/jpokerstats_er_simple.png" alt="Simply Entity/Relationship Diagram for JPokerStats" align="middle" /></p>
<p>This tutorial assumes that you are already familiar with object-relational-mapping technologies or at least the concept behind it.</p>
<h3>Setting up the project</h3>
<p>We start off with the application by creating a skeleton using the Maven archetype plugin. We can achieve this by executing:</p><pre class="crayon-plain-tag">mvn archetype:create -DgroupId=de.icanmakeit.jpokerstats -DartifactId=jpokerstats -Dversion=0.1-SNAPSHOT -DarchetypeArtifactId=maven-archetype-webapp</pre><p></p>
<p>This will create some basic files for us such as the Maven project description (<code>pom.xml</code>), the necessary directory structures and the web specific files such as the <code>web.xml</code> which we are going to modify instantly by replacing the 2.3 header declaration with a 2.4 declaration. You might object and say that version 2.5 web applications are currently state of the art. You&#8217;re right on this but we&#8217;ll leave the version set to 2.4 for now and I will explain why in a later part of this tutorial.</p>
<h3>Dependencies</h3>
<p>Next we&#8217;re going to define the projects dependencies. It already has a dependency on JUnit that we upgrade from version 3.8.1 to version 4.4 to be able to use annotations for our tests.<br />
As the title of this tutorial states, we are going to use a lot of external libraries as can be seen in the following list:</p>
<ul>
<li>JSF API/RI 1.2_07</li>
<li>Facelets 1.1.14</li>
<li>JPA 1.0</li>
<li>Spring Framework 2.5.1</li>
<li>Apache Shale 1.1.0-SNAPSHOT</li>
</ul>
<p>We will be adding the needed artifacts to the <code>pom.xml</code>. Notice that I have declared some properties containing various version numbers for libraries. Most of them consist of more than one artifact, so referencing a single version property make it easier to upgrade them at a later time. Some artifacts are not available in the central repository and need to be downloaded from other repositories. I have included those in the <code>pom.xml</code>, but after adding dependencies you should run <code>mvn compile</code> to check if they are all available.<br />
Some artifact scopes are defined as <code>provided</code>. This means that Maven supplies those libraries at compile time but does not include them in a software release file (in this case the web archive or <code>WAR</code>), assuming that the servlet container or application server will provide them. If your server does not include those libraries (e.g. Tomcat doesn&#8217;t) you might want to change the scope from <code>provided</code> to <code>compile</code>.</p>
<h3>The first Java code</h3>
<p>To finish off this part of the tutorial, we&#8217;re finally going to write some Java code for the JPA layer of our project. So I create three classes, <code>Game</code>, <code>Player</code> and <code>GameResult</code> an put some attributes in them. As JPA objects are simple Java beans or POJOs, this step is quite easy. To simply the work with attributes such as <code>gender</code> or <code>gameType</code>, we define some enumerations to help us with these values.<br />
After writing the Java classes, we need to instrument them for use with JPA. This tutorial is going to use HSQLDB for data storage because it can be run out of the box and needs no additional setup. But since we are using JPA you could use any other supported database such as MySQL, postgreSQL or Oracle.</p>
<p>The primary key is going to be generated and we leave the decision on how to do this to JPA. The JPA implementation will use the method it thinks is most suitable for the database used.</p><pre class="crayon-plain-tag">@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public int getId()
{
return _id;
}</pre><p></p>
<p>JPA is able to translate enumerations to database value either by saving the values position (e.g. <code>2</code>) within the enumeration or by saving the value itself as string (e.g. <code>MALE</code>). I prefer the latter method because it makes the database easier to read and those few extra bytes do not actually matter (at least to me). So we&#8217;re going to specify this behavior on the getter for gender.</p>
<p></p><pre class="crayon-plain-tag">@Enumerated(EnumType.STRING)
public Gender getGender()
{
return _gender;
}</pre><p></p>
<p>We&#8217;ll use the same way of annotating fields for the other classes as well. In addition, JPA requires us the specify how to store temporal values such as the date on which a game took place. We choose <code>DATE</code> for real date values where the time doesn&#8217;t matter and choose <code>TIME</code> when we want only the times saved. In case we needed date and time saved together, we would specify <code>TIMESTAMP</code>.</p><pre class="crayon-plain-tag">@Temporal(value=TemporalType.DATE)
public Date getDate()
{
return _date;
}

@Temporal(value=TemporalType.TIME)
public Date getStartTime()
{
return _startTime;
}</pre><p></p>
<p>By this, we are done for this part of the tutorial. In the next part, we will introduce JPA based relationships between the entities and configure the database and Spring for our application.</p>
<!-- Beginn von `social share privacy by smeagol.de´ --><div id="socialshareprivacy8"></div>
			<script type="text/javascript">
			(function($){
				var options = {"info_link":"http:\/\/heise.de\/-1333879","txt_help":"Wenn Sie diese Felder durch einen Klick aktivieren, werden Informationen an Facebook, Twitter oder Google in die USA \u00fcbertragen und unter Umst\u00e4nden auch dort gespeichert. N\u00e4heres erfahren Sie durch einen Klick auf das <em>i<\/em>.","settings_perma":"Dauerhaft aktivieren und Daten\u00fcber\u00adtragung zustimmen:","cookie_path":"\/","cookie_expire":"365","cookie_domain":"","css_path":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/socialshareprivacy.css","oben":"nein","overall":"ja","ausschluss_private":"nein","services":{"facebook":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_facebook.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Facebook senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_fb_off":"nicht mit Facebook verbunden","txt_fb_on":"mit Facebook verbunden","display_name":"Facebook","referrer_track":"","language":"de_DE"},"twitter":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_twitter.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Twitter senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_twitter_off":"nicht mit Twitter verbunden","txt_twitter_on":"mit Twitter verbunden","display_name":"Twitter","referrer_track":"","tweet_text":"Developing lightweight Java web applications - Part 2: Getting started "},"gplus":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_gplus.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Google+ senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_gplus_off":"nicht mit Google+ verbunden","txt_gplus_on":"mit Google+ verbunden","display_name":"Google+","referrer_track":"","language":"de"}},"uri":"http:\/\/www.icanmakeit.de\/2008\/02\/08\/developing-lightweight-java-web-applications-part-2-getting-started\/"};
				options.cookie_domain = document.location.host;
				$(document).ready(function(){
					$('#socialshareprivacy8').socialSharePrivacy(options);
				});
			})(jQuery);
			</script>
		<!-- Ende von `social share privacy by smeagol.de´ -->
	Tags: <a href="http://www.icanmakeit.de/tag/developing-java-web-applications/" title="developing java web applications" rel="tag">developing java web applications</a>, <a href="http://www.icanmakeit.de/tag/facelets/" title="facelets" rel="tag">facelets</a>, <a href="http://www.icanmakeit.de/tag/jpa/" title="jpa" rel="tag">jpa</a>, <a href="http://www.icanmakeit.de/tag/jsf/" title="jsf" rel="tag">jsf</a>, <a href="http://www.icanmakeit.de/tag/shale/" title="shale" rel="tag">shale</a>, <a href="http://www.icanmakeit.de/tag/spring/" title="spring" rel="tag">spring</a>, <a href="http://www.icanmakeit.de/tag/web/" title="web" rel="tag">web</a><br />
]]></content:encoded>
			<wfw:commentRss>http://www.icanmakeit.de/2008/02/08/developing-lightweight-java-web-applications-part-2-getting-started/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Developing lightweight Java web applications with JSF, Facelets, JPA, Spring and Shale &#8211; Part 1: The Primer</title>
		<link>http://www.icanmakeit.de/2008/01/31/developing-lightweight-java-web-applications-with-jsf-facelets-jpa-spring-and-shale-part-1-the-primer/</link>
		<comments>http://www.icanmakeit.de/2008/01/31/developing-lightweight-java-web-applications-with-jsf-facelets-jpa-spring-and-shale-part-1-the-primer/#comments</comments>
		<pubDate>Wed, 30 Jan 2008 23:13:06 +0000</pubDate>
		<dc:creator>Hendrik Busch</dc:creator>
				<category><![CDATA[Entwicklung]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[developing java web applications]]></category>
		<category><![CDATA[facelets]]></category>
		<category><![CDATA[jpa]]></category>
		<category><![CDATA[jsf]]></category>
		<category><![CDATA[shale]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.icanmakeit.de/2008/01/31/developing-lightweight-java-web-applications-with-jsf-facelets-jpa-spring-and-shale-part-1-the-primer/</guid>
		<description><![CDATA[Update 2012-05-28: I have deleted the project over at Google Code since I never finished it and it is outdated by now. Please refer to other, more state-of-the-art tutorials regarding building web applications with Java. In addition, comments are also closed on these posts. What is this all about? (Motivation) If you&#8217;ve ever dug deeper [...]]]></description>
				<content:encoded><![CDATA[<p><em><strong>Update 2012-05-28:</strong> I have deleted the project over at Google Code since I never finished it and it is outdated by now. Please refer to other, more state-of-the-art tutorials regarding building web applications with Java. In addition, comments are also closed on these posts.</em></p>
<h3>What is this all about? (Motivation)</h3>
<p align="justify">If you&#8217;ve ever dug deeper into developing web applications with JSF or have developed Java web applications in the past using other frameworks and libraries, e.g. Turbine, Velocity, Struts, etc., you will soon find that JSF itself has a a nice concept but provides only the most basic functionality.</p>
<p align="justify">In JSF itself you have a strict seperation of concerns and a nicely split MVC architecture, but for a fully fledged application that uses persistent data, pluggable components and a templating system, JSF on its own just isn&#8217;t enough.</p>
<p align="justify">You could now start creating a new Java 5 EE application, building several components (JPA, EJB, etc.), bundle them into an EAR file and deploy them to the Glassfish application server. But we won&#8217;t. Don&#8217;t misunderstand me: there&#8217;s nothing wrong with writing a full Java Enterprise application and I salute to those who do, but for most developers such a setup is just overkill (whereas a simple Servlet is not enough). But this series is about lightweight applications. Please note that &#8220;weightâ€œ is not measured in bytes but in the amount of stuff you have to write to get things done.</p>
<p align="justify">This series describes a different approach to Java web applications and is inspired by the <a title="The facesgoodies project at Google Code" href="http://code.google.com/p/facesgoodies/">facesgoodies project</a> developed by<a title="Matthias Wessendorf's blog" href="http://matthiaswessendorf.wordpress.com/"> Matthias Wessendorf</a>. This project is a kickstart project for developing JSF applications including Spring and Shale using the JPA persistence layer. Unfortunately it is not document very well and somewhat hard to understand for those who have never used Spring or configured a more complex web application or a persistence unit.</p>
<p align="justify">This series/tutorial is designed to help you design next generation web applications using modern, community supported technologies.</p>
<p><span id="more-167"></span></p>
<h3>Take a look around (The libraries used)</h3>
<h4>Java Server Faces (JSF)</h4>
<p align="justify">Since you need at least a basic understanding of JSF in order to understand this tutorial I will not go into further details when describing JSF. Let&#8217;s just say that Java Server Faces is the recommendable technology for developing web applications with Java. You can read more about JSF by reading the <a href="http://java.sun.com/javaee/javaserverfaces/">official documentation</a>.</p>
<h4>Facelets</h4>
<p align="justify">Facelets is a templating library for use with JSF. All pages are written in pure XHTML by utilising the XML namespace features to add additional JSF or JSP libraries. Basically you define a couple of named placeholders in your template file(s) and fill them later on with page specific content. Facelets offers a simple API with not more than ten tags for use in your templates and pages. And believe it or not, you usually don&#8217;t need more than four of them. You can read more on Facelets by visiting the <a href="https://facelets.dev.java.net/">official project site</a>, reading the <a href="https://facelets.dev.java.net/nonav/docs/dev/docbook.html">documentation</a> or the <a href="http://www.jsfcentral.com/articles/facelets_1.html">Inside JSF article series</a> over at JSF Central.</p>
<h4>Java Persistence API (JPA)</h4>
<p align="justify">JPA is a standardized API specification for Java persistence. It allows you to create simple POJO beans and map them to the database of your choice by adding some simple annotations. Since JPA is only a specification, we will also need a library that implements this specification. You may choose from several implementation, the two most popular are Hibernate and Toplink. This tutorial will use Toplink Essentials, the library that comes bundled with the Glassfish application server.</p>
<h4>Spring</h4>
<p align="justify"><a href="http://www.springframework.org/">Spring</a> is a fully fledged, modular Java Enterprise framework. What does that mean? In this tutorial we will not use all modules offered by Spring. We will use the IoC container to reduce the applications complexity when it comes to configuring objects and services. It will also help us to design decoupled components that can be switched later against other implementations, giving us a more flexible design.</p>
<p align="justify">We want our application to run on simple Servlet containers such as Jetty or Tomcat, therefor we cannot let the application server manage our database connections and transactions. This functionality will instead be provided by the Spring DAO and the Spring Transaction module.</p>
<p align="justify">Spring is â€“ contrary to many other open source projects â€“ very well documented. You might consider reading the <a href="http://static.springframework.org/spring/docs/2.5.x/reference/index.html">developer manual</a> or at least parts of it.</p>
<h4>Apache Shale</h4>
<p align="justify"><a href="http://shale.apache.org/">Shale</a> is a set of modules that are built around the basic JSF implementation and enhance it by various features. For the beginning, we will only use Shale Core and Shale Views. Views provides the application with a real controller layer in accordance with the MVC principle, replacing the JSF &#8220;glue objectsâ€œ proposed by many JSF tutorials.</p>
<h4>Maven 2</h4>
<p align="justify">In order to reduce the hassle of managing the classpath, dependencies, directories and other aspects of Java software development, this project will be built using Maven 2. You might consider making yourself familiar the way Maven works by <a href="http://maven.apache.org/guides/getting-started/maven-in-five-minutes.html">reading the getting started documentation</a>.</p>
<!-- Beginn von `social share privacy by smeagol.de´ --><div id="socialshareprivacy10"></div>
			<script type="text/javascript">
			(function($){
				var options = {"info_link":"http:\/\/heise.de\/-1333879","txt_help":"Wenn Sie diese Felder durch einen Klick aktivieren, werden Informationen an Facebook, Twitter oder Google in die USA \u00fcbertragen und unter Umst\u00e4nden auch dort gespeichert. N\u00e4heres erfahren Sie durch einen Klick auf das <em>i<\/em>.","settings_perma":"Dauerhaft aktivieren und Daten\u00fcber\u00adtragung zustimmen:","cookie_path":"\/","cookie_expire":"365","cookie_domain":"","css_path":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/socialshareprivacy.css","oben":"nein","overall":"ja","ausschluss_private":"nein","services":{"facebook":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_facebook.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Facebook senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_fb_off":"nicht mit Facebook verbunden","txt_fb_on":"mit Facebook verbunden","display_name":"Facebook","referrer_track":"","language":"de_DE"},"twitter":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_twitter.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Twitter senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_twitter_off":"nicht mit Twitter verbunden","txt_twitter_on":"mit Twitter verbunden","display_name":"Twitter","referrer_track":"","tweet_text":"Developing lightweight Java web applications with JSF, Facelets, JPA, Spring and Shale - Part 1: The Primer "},"gplus":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_gplus.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Google+ senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_gplus_off":"nicht mit Google+ verbunden","txt_gplus_on":"mit Google+ verbunden","display_name":"Google+","referrer_track":"","language":"de"}},"uri":"http:\/\/www.icanmakeit.de\/2008\/01\/31\/developing-lightweight-java-web-applications-with-jsf-facelets-jpa-spring-and-shale-part-1-the-primer\/"};
				options.cookie_domain = document.location.host;
				$(document).ready(function(){
					$('#socialshareprivacy10').socialSharePrivacy(options);
				});
			})(jQuery);
			</script>
		<!-- Ende von `social share privacy by smeagol.de´ -->
	Tags: <a href="http://www.icanmakeit.de/tag/developing-java-web-applications/" title="developing java web applications" rel="tag">developing java web applications</a>, <a href="http://www.icanmakeit.de/tag/facelets/" title="facelets" rel="tag">facelets</a>, <a href="http://www.icanmakeit.de/tag/java/" title="Java" rel="tag">Java</a>, <a href="http://www.icanmakeit.de/tag/jpa/" title="jpa" rel="tag">jpa</a>, <a href="http://www.icanmakeit.de/tag/jsf/" title="jsf" rel="tag">jsf</a>, <a href="http://www.icanmakeit.de/tag/shale/" title="shale" rel="tag">shale</a>, <a href="http://www.icanmakeit.de/tag/spring/" title="spring" rel="tag">spring</a>, <a href="http://www.icanmakeit.de/tag/web/" title="web" rel="tag">web</a><br />
]]></content:encoded>
			<wfw:commentRss>http://www.icanmakeit.de/2008/01/31/developing-lightweight-java-web-applications-with-jsf-facelets-jpa-spring-and-shale-part-1-the-primer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Manche Dinge blieben besser unentdeckt</title>
		<link>http://www.icanmakeit.de/2007/08/17/manche-dinge-blieben-besser-unentdeckt/</link>
		<comments>http://www.icanmakeit.de/2007/08/17/manche-dinge-blieben-besser-unentdeckt/#comments</comments>
		<pubDate>Fri, 17 Aug 2007 12:53:02 +0000</pubDate>
		<dc:creator>Hendrik Busch</dc:creator>
				<category><![CDATA[Arbeit]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[quellcode]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[unnötig]]></category>

		<guid isPermaLink="false">http://www.icanmakeit.de/2007/08/17/manche-dinge-blieben-besser-unentdeckt/</guid>
		<description><![CDATA[Softwareprojekte haben die unangenehme Eigenschaft bei längerer Lagerzeit in der Versionsverwaltung zu degenerieren. Dieses Phänomen liegt allerdings eher daran, dass man sich als Programmierer weiterentwickelt und dazulernt, während der Code gleich (schlecht) bleibt. Manchmal gibt es aber auch Codeteile, die von vorneherein sehr merkwürdig sind: [crayon-51a0979354b75/] Es ginge auch etwas einfacher: [crayon-51a0979354bbf/] Solche Dinge will [...]]]></description>
				<content:encoded><![CDATA[<p>Softwareprojekte haben die unangenehme Eigenschaft bei längerer Lagerzeit in der <a title="Was ist eine Versionsverwaltung?" href="http://de.wikipedia.org/wiki/Versionsverwaltung">Versionsverwaltung</a> zu degenerieren. Dieses Phänomen liegt allerdings eher daran, dass man sich als Programmierer weiterentwickelt und dazulernt, während der Code gleich (schlecht) bleibt.</p>
<p>Manchmal gibt es aber auch Codeteile, die von vorneherein sehr merkwürdig sind:</p>
<p></p><pre class="crayon-plain-tag">final Date heute = new SimpleDateFormat(&quot;HH:mm dd.MM.yyyy&quot;).parse(new
SimpleDateFormat(&quot;HH:mm dd.MM.yyyy&quot;).format(new Date()));</pre><p></p>
<p>Es ginge auch etwas einfacher:</p>
<p></p><pre class="crayon-plain-tag">final Date heute = new Date();</pre><p></p>
<p>Solche Dinge will man dann doch lieber nie wieder sehen&#8230;</p>
<!-- Beginn von `social share privacy by smeagol.de´ --><div id="socialshareprivacy12"></div>
			<script type="text/javascript">
			(function($){
				var options = {"info_link":"http:\/\/heise.de\/-1333879","txt_help":"Wenn Sie diese Felder durch einen Klick aktivieren, werden Informationen an Facebook, Twitter oder Google in die USA \u00fcbertragen und unter Umst\u00e4nden auch dort gespeichert. N\u00e4heres erfahren Sie durch einen Klick auf das <em>i<\/em>.","settings_perma":"Dauerhaft aktivieren und Daten\u00fcber\u00adtragung zustimmen:","cookie_path":"\/","cookie_expire":"365","cookie_domain":"","css_path":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/socialshareprivacy.css","oben":"nein","overall":"ja","ausschluss_private":"nein","services":{"facebook":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_facebook.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Facebook senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_fb_off":"nicht mit Facebook verbunden","txt_fb_on":"mit Facebook verbunden","display_name":"Facebook","referrer_track":"","language":"de_DE"},"twitter":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_twitter.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Twitter senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_twitter_off":"nicht mit Twitter verbunden","txt_twitter_on":"mit Twitter verbunden","display_name":"Twitter","referrer_track":"","tweet_text":"Manche Dinge blieben besser unentdeckt "},"gplus":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_gplus.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Google+ senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_gplus_off":"nicht mit Google+ verbunden","txt_gplus_on":"mit Google+ verbunden","display_name":"Google+","referrer_track":"","language":"de"}},"uri":"http:\/\/www.icanmakeit.de\/2007\/08\/17\/manche-dinge-blieben-besser-unentdeckt\/"};
				options.cookie_domain = document.location.host;
				$(document).ready(function(){
					$('#socialshareprivacy12').socialSharePrivacy(options);
				});
			})(jQuery);
			</script>
		<!-- Ende von `social share privacy by smeagol.de´ -->
	Tags: <a href="http://www.icanmakeit.de/tag/quellcode/" title="quellcode" rel="tag">quellcode</a>, <a href="http://www.icanmakeit.de/tag/software/" title="software" rel="tag">software</a>, <a href="http://www.icanmakeit.de/tag/unnotig/" title="unnötig" rel="tag">unnötig</a><br />
]]></content:encoded>
			<wfw:commentRss>http://www.icanmakeit.de/2007/08/17/manche-dinge-blieben-besser-unentdeckt/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Realistische Betrachung: Administratoren vs. Entwickler</title>
		<link>http://www.icanmakeit.de/2007/01/12/realistische-betrachung-administratoren-vs-entwickler/</link>
		<comments>http://www.icanmakeit.de/2007/01/12/realistische-betrachung-administratoren-vs-entwickler/#comments</comments>
		<pubDate>Fri, 12 Jan 2007 09:58:14 +0000</pubDate>
		<dc:creator>Hendrik Busch</dc:creator>
				<category><![CDATA[Arbeit]]></category>
		<category><![CDATA[Computer & Technik]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.icanmakeit.de/2007/01/12/realistische-betrachung-administratoren-vs-entwickler/</guid>
		<description><![CDATA[Im ONJava.com-Blog von O&#8217;Reilly wurde heute ein interessanter Artikel Timothy O&#8217;Brien veröffentlicht, der sich mit den häufig auftretenden Konflikten zwischen Entwicklern und Administratoren beschäftigt. Er nennt sich Java&#8217;s &#8220;Operations&#8221; Problems und bietet eine sehr interessante Betrachtungsweise eines typischen Konflikts, den vermutlich jeder Entwickler in größeren Unternehmen schon einmal erlebt hat: der Entwickler möchte etwas, der [...]]]></description>
				<content:encoded><![CDATA[<p>Im <a title="O'Reilly Network Java Webseite" href="http://www.onjava.com/">ONJava.com-Blog</a> von O&#8217;Reilly wurde heute ein interessanter Artikel Timothy O&#8217;Brien veröffentlicht, der sich mit den häufig auftretenden Konflikten zwischen Entwicklern und Administratoren beschäftigt. Er nennt sich <a title="Artikel Javas Operations Problem bei ONJava.com" href="http://www.oreillynet.com/onjava/blog/2007/01/javas_operations_problem.html"><em>Java&#8217;s &#8220;Operations&#8221; Problems</em></a> und bietet eine sehr interessante Betrachtungsweise eines typischen Konflikts, den vermutlich jeder Entwickler in größeren Unternehmen schon einmal erlebt hat: der Entwickler möchte etwas, der Administrator möchte genau das nicht oder auch umgekehrt. Dies führt meist zu größeren Konflikten, die mehr einem Glaubenskrieg ähneln als einer sachlichen Diskussion.</p>
<p>Der Artikel handelt in diesem Fall speziell von Java, dass durch seine Abstraktion von den eigentlichen Systemgrundlagen eine zusätzliche Hürde mit einbringt, die vorgestellten Thesen und Lösungsansätze sind allerdings allgemeingültig.<br />
O&#8217;Brien sieht den Grund für die Konflikte in einer zu starren Abgrenzung zwischen den beiden Personengruppen, deren Tätigkeit thematisch überlappt, deren Zuständigkeiten aber hart getrennt sind. Er skizziert eine ideale Verfahrensweise und und stellt einen zweistufigen Plan vor, wie man die Reibung zwischen Administration und Entwicklung verringert.</p>
<blockquote><p><strong>The Short-term Solution: Hold Hands and Sing a Song</strong></p>
<p>If your organization has friction between Operations and Development, a quick short-term fix is to nominate one person from each team to serve as a liaison to the other group.</p></blockquote>
<p>Diesen Schritt haben wir in unserer Firma bereits vollzogen und es hat sich als sehr gut heraus gestellt, eine direkte Schnittstelle zur anderen Abteilung zu haben. Probleme aber auch Anforderungen sind so viel einfacher zu klären.</p>
<blockquote><p><strong>The Long-term Solution: Stop Developing Applications, Stop Administering Systems</strong></p>
<p>â€¦do away with System Administrators and Application Developers. No, really, I mean that.</p></blockquote>
<p>Der Ausschnitt selber klingt recht drastisch, er beschreibt aber eine thematische Annäherung von Administratoren und Entwicklern und den Wandel der klassischen Rollen im Verlauf der Zeit und im Zuge der technischen Entwicklung.</p>
<p>Insgesamt ein sehr lesenswerter Artikel, den ich jedem Administrator und jedem Entwickler nur empfehlen kann.</p>
<!-- Beginn von `social share privacy by smeagol.de´ --><div id="socialshareprivacy14"></div>
			<script type="text/javascript">
			(function($){
				var options = {"info_link":"http:\/\/heise.de\/-1333879","txt_help":"Wenn Sie diese Felder durch einen Klick aktivieren, werden Informationen an Facebook, Twitter oder Google in die USA \u00fcbertragen und unter Umst\u00e4nden auch dort gespeichert. N\u00e4heres erfahren Sie durch einen Klick auf das <em>i<\/em>.","settings_perma":"Dauerhaft aktivieren und Daten\u00fcber\u00adtragung zustimmen:","cookie_path":"\/","cookie_expire":"365","cookie_domain":"","css_path":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/socialshareprivacy.css","oben":"nein","overall":"ja","ausschluss_private":"nein","services":{"facebook":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_facebook.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Facebook senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_fb_off":"nicht mit Facebook verbunden","txt_fb_on":"mit Facebook verbunden","display_name":"Facebook","referrer_track":"","language":"de_DE"},"twitter":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_twitter.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Twitter senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_twitter_off":"nicht mit Twitter verbunden","txt_twitter_on":"mit Twitter verbunden","display_name":"Twitter","referrer_track":"","tweet_text":"Realistische Betrachung: Administratoren vs. Entwickler "},"gplus":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_gplus.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Google+ senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_gplus_off":"nicht mit Google+ verbunden","txt_gplus_on":"mit Google+ verbunden","display_name":"Google+","referrer_track":"","language":"de"}},"uri":"http:\/\/www.icanmakeit.de\/2007\/01\/12\/realistische-betrachung-administratoren-vs-entwickler\/"};
				options.cookie_domain = document.location.host;
				$(document).ready(function(){
					$('#socialshareprivacy14').socialSharePrivacy(options);
				});
			})(jQuery);
			</script>
		<!-- Ende von `social share privacy by smeagol.de´ -->Dieser Beitrag ist nicht getaggt.]]></content:encoded>
			<wfw:commentRss>http://www.icanmakeit.de/2007/01/12/realistische-betrachung-administratoren-vs-entwickler/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JPA: Bidirektionale OneToMany/ManyToOne Beziehungen</title>
		<link>http://www.icanmakeit.de/2006/11/14/jpa-bidirektionale-onetomanymanytoone-beziehungen/</link>
		<comments>http://www.icanmakeit.de/2006/11/14/jpa-bidirektionale-onetomanymanytoone-beziehungen/#comments</comments>
		<pubDate>Tue, 14 Nov 2006 11:23:50 +0000</pubDate>
		<dc:creator>Hendrik Busch</dc:creator>
				<category><![CDATA[Arbeit]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[enterprise]]></category>

		<guid isPermaLink="false">http://www.icanmakeit.de/2006/11/14/jpa-bidirektionale-onetomanymanytoone-beziehungen/</guid>
		<description><![CDATA[Dieser Beitrag soll in erster Linie für mich als Gedächtnisstütze dienen. Als Nachwirkung der Schulung beschäftige ich mich nicht nur mit Enterprise Java Beans (EJB) sondern auch mit der Java Persistence API (JPA), dem &#8220;neuen&#8221; Persistenzstandard, der im gleichen Prozess wie EJB entworfen wurde. Im Entwicklerforum von java.net findet sich ein passender Satz zu dem [...]]]></description>
				<content:encoded><![CDATA[<p>Dieser Beitrag soll in erster Linie für mich als Gedächtnisstütze dienen. Als Nachwirkung der Schulung beschäftige ich mich nicht nur mit Enterprise Java Beans (EJB) sondern auch mit der <a title="Technologieseite zu JPA bei Sun" href="http://java.sun.com/javaee/technologies/entapps/persistence.jsp">Java Persistence API</a> (JPA), dem &#8220;neuen&#8221; Persistenzstandard, der im gleichen <a title="Java Community Process 220: EJB" href="http://www.jcp.org/en/jsr/detail?id=220">Prozess</a> wie EJB entworfen wurde. Im Entwicklerforum von java.net findet sich ein passender Satz zu dem Thema, den jemand als Antwort bekam, <a title="Thread im Java.net-Forum" href="http://forums.java.net/jive/thread.jspa?threadID=19150">der sich darüber beklagt</a>, dass JPA so unvorhersehbar arbeiten würde:</p>
<blockquote><p>I doubt that it&#8217;s a matter of it being &#8220;unpredictable&#8221; so much as it is getting over the learning curve.</p></blockquote>
<p>Genau in dieser Lernkurve stecke ich auch derzeit noch und erlerne JPA gerade im Eigenstudium. Für erfahrene  Entwickler ist dieser Beitrag daher nicht gedacht.</p>
<h3>Das Problem</h3>
<p>Es existieren zwei Entitäten, Benutzer und Gruppe. Jeder Benutzer kann einer Gruppe angehören, folglich können zu einer Gruppe mehrere Benutzer gehören. Beide Entitäten sind wie folgt definiert:</p>
<p></p><pre class="crayon-plain-tag">@Entity
public class Benutzer
{
    private String _name;
    private String _vorname;
    private String _benutzername;
    private String _email;
    private int _id;
    private Gruppe _gruppe;

    /*
     * Es folgen die ganze Getter und Setter fuer die
     * obigen Eigenschaften.
     * [...]
     */
    
    @ManyToOne
    public Gruppe getGruppe()
    {
        return _gruppe;
    }

    public void setGruppe(final Gruppe gruppe)
    {
        _gruppe = gruppe;
    }
}</pre><p></p>
<p></p><pre class="crayon-plain-tag">@Entity
public class Gruppe
{
    private int _id;
    private String _name;
    private String _beschreibung;
    private Set&lt;Benutzer&gt; _benutzer = new HashSet&lt;Benutzer&gt;();
            
    /*
     * Es folgen die ganze Getter und Setter fuer die
     * obigen Eigenschaften.
     * [...]
     */            
            
    @OneToMany(mappedBy = &quot;gruppe&quot;)
    public Set&lt;Benutzer&gt; getBenutzer()
    {
        return _benutzer;
    }

    public void setBenutzer(final Set&lt;Benutzer&gt; benutzer)
    {
        _benutzer = benutzer;
    }
}</pre><p></p>
<p>Ein kleines Programm geht jetzt her und erzeugt drei Benutzer und zwei Gruppen. Zwei der Benutzer werden der ersten Gruppe zugewiesen, der dritte der anderen Gruppe.</p>
<p></p><pre class="crayon-plain-tag">EntityManager em = createEntityManager();

        final Gruppe g1 = createGruppe(&quot;Demokraten&quot;, &quot;Die Partei der Demokraten&quot;);
        final Gruppe g2 = createGruppe(&quot;Republikaner&quot;, &quot;Die Partei der Republikaner&quot;);

        em.persist(g1);
        em.persist(g2);
        
        final Benutzer b1 = createBenutzer(&quot;Obama&quot;, &quot;Barack&quot;, &quot;bobama&quot;, &quot;obama@senate.gov&quot;);
        final Benutzer b2 = createBenutzer(&quot;McCain&quot;, &quot;John&quot;, &quot;jmccain&quot;, &quot;mccain@senate.gov&quot;);
        final Benutzer b3 = createBenutzer(&quot;Clinton&quot;, &quot;Hillary&quot;, &quot;hclinton&quot;, &quot;clinton@senate.gov&quot;);

        b1.setGruppe(g1);
        b2.setGruppe(g2);
        b3.setGruppe(g1);

        em.persist(b1);
        em.persist(b2);
        em.persist(b3);</pre><p></p>
<p>Danach geht das Programm her und lädt die Datensätze wieder aus der Datenbank und gibt sie und ihre Beziehungen aus.</p>
<p></p><pre class="crayon-plain-tag">final List result = em.createQuery(&quot;SELECT b FROM Benutzer b ORDER BY b.name&quot;).getResultList();
        
        for (final Object b : result)
        {
            if (b instanceof Benutzer)
            {
                final Benutzer benutzer = (Benutzer) b;
                LOG.info(benutzer);
                LOG.info(&quot;\t+--&gt; &quot; + benutzer.getGruppe());
            }
        }

        final List result2 = em.createQuery(&quot;SELECT g FROM Gruppe g&quot;).getResultList();
        
        for (final Object o : result2)
        {
            if (o instanceof Gruppe)
            {
                final Gruppe g = (Gruppe) o;
                LOG.info(g);
                LOG.info(&quot;Benutzerzahl: &quot; + g.getBenutzer().size());

                for(final Benutzer b : g.getBenutzer())
                {
                    LOG.info(&quot;\t+--&gt; &quot; + b);
                }
            }
        }</pre><p></p>
<p>Es entsteht folgende Ausgabe:</p>
<p></p><pre class="crayon-plain-tag">JPAClient.showDemo()@107: Benutzer [2902]: Clinton, Hillary (hclinton, clinton@senate.gov)
JPAClient.showDemo()@108: 	+--> Gruppe [2800]: Demokraten (Die Partei der Demokraten)
JPAClient.showDemo()@107: Benutzer [2901]: McCain, John (jmccain, mccain@senate.gov)
JPAClient.showDemo()@108: 	+--> Gruppe [2801]: Republikaner (Die Partei der Republikaner)
JPAClient.showDemo()@107: Benutzer [2900]: Obama, Barack (bobama, obama@senate.gov)
JPAClient.showDemo()@108: 	+--> Gruppe [2800]: Demokraten (Die Partei der Demokraten)
JPAClient.showDemo()@120: Gruppe [2800]: Demokraten (Die Partei der Demokraten)
JPAClient.showDemo()@121: Benutzerzahl: 0
JPAClient.showDemo()@120: Gruppe [2801]: Republikaner (Die Partei der Republikaner)
JPAClient.showDemo()@121: Benutzerzahl: 0</pre><p> </p>
<p>Das Problem ist offensichtlich: Während jeder Benutzer eine Referenz auf das ihm zugeordnete Gruppenobjekt hält, kennt keine der Gruppen die Benutzer, die ihr zugeordnet sind.</p>
<h3>Die Lösung</h3>
<p>Bidirektionale Beziehungen sollten eigentlich anders funktionieren. Das oben geschilderte Problem ist das, was auch vielen anderen wegen seiner Struktur Probleme bereitet. Die Beziehung funktioniert beidseitig und solange der gleiche EntityManager läuft, können Objekte vom JPA-Provider zwischengespeichert werden. Daher reicht ein einseitiges Zuweisen nicht aus, vielmehr muss in diesem Fall auch der Gruppe der Benutzer zugewiesen werden. Erst dann sind die zwischengespeicherten Objekte vollständig.</p>
<p></p><pre class="crayon-plain-tag">EntityManager em = createEntityManager();

        final Gruppe g1 = createGruppe(&quot;Demokraten&quot;, &quot;Die Partei der Demokraten&quot;);
        final Gruppe g2 = createGruppe(&quot;Republikaner&quot;, &quot;Die Partei der Republikaner&quot;);

        final Benutzer b1 = createBenutzer(&quot;Obama&quot;, &quot;Barack&quot;, &quot;bobama&quot;, &quot;obama@senate.gov&quot;);
        final Benutzer b2 = createBenutzer(&quot;McCain&quot;, &quot;John&quot;, &quot;jmccain&quot;, &quot;mccain@senate.gov&quot;);
        final Benutzer b3 = createBenutzer(&quot;Clinton&quot;, &quot;Hillary&quot;, &quot;hclinton&quot;, &quot;clinton@senate.gov&quot;);

        b1.setGruppe(g1);
        b2.setGruppe(g2);
        b3.setGruppe(g1);
        
        g1.getBenutzer().add(b1);
        g2.getBenutzer().add(b2);
        g1.getBenutzer().add(b3)

        em.persist(b1);
        em.persist(b2);
        em.persist(b3);
        
        em.persist(g1);
        em.persist(g2);</pre><p></p>
<p>Folgende Ausgabe zeigt das Ergebnis der Änderung:</p>
<p></p><pre class="crayon-plain-tag">JPAClient.showDemo()@107: Benutzer [2952]: Clinton, Hillary (hclinton, clinton@senate.gov)
JPAClient.showDemo()@108: 	+--> Gruppe [2850]: Demokraten (Die Partei der Demokraten)
JPAClient.showDemo()@107: Benutzer [2951]: McCain, John (jmccain, mccain@senate.gov)
JPAClient.showDemo()@108: 	+--> Gruppe [2851]: Republikaner (Die Partei der Republikaner)
JPAClient.showDemo()@107: Benutzer [2950]: Obama, Barack (bobama, obama@senate.gov)
JPAClient.showDemo()@108: 	+--> Gruppe [2850]: Demokraten (Die Partei der Demokraten)
JPAClient.showDemo()@112: Lade Gruppen
JPAClient.showDemo()@120: Gruppe [2850]: Demokraten (Die Partei der Demokraten)
JPAClient.showDemo()@121: Benutzerzahl: 2
JPAClient.showDemo()@126: 	+--> Benutzer [2952]: Clinton, Hillary (hclinton, clinton@senate.gov)
JPAClient.showDemo()@126: 	+--> Benutzer [2950]: Obama, Barack (bobama, obama@senate.gov)
JPAClient.showDemo()@120: Gruppe [2851]: Republikaner (Die Partei der Republikaner)
JPAClient.showDemo()@121: Benutzerzahl: 1
JPAClient.showDemo()@126: 	+--> Benutzer [2951]: McCain, John (jmccain, mccain@senate.gov)</pre><p></p>
<p>Diese Art der Zuweisung im Code eines Client-Programms ist natürlich etwas unschön, sinnvoller wäre es, diese Zuweisung gleich im entsprechenden Setter der Benutzerklasse zu machen.</p>
<p></p><pre class="crayon-plain-tag">public void setGruppe(final Gruppe gruppe)
    {
        _gruppe = gruppe;
        if (null != _gruppe.getBenutzer() &amp;&amp; !_gruppe.getBenutzer().contains(this))
        {
            _gruppe.getBenutzer().add(this);
        }
    }</pre><p></p>
<p>Das Problem erübrigt sich auch, wenn man einen anderen EntityManager verwendet als den, der die Objekte persistiert hat.</p>
<!-- Beginn von `social share privacy by smeagol.de´ --><div id="socialshareprivacy16"></div>
			<script type="text/javascript">
			(function($){
				var options = {"info_link":"http:\/\/heise.de\/-1333879","txt_help":"Wenn Sie diese Felder durch einen Klick aktivieren, werden Informationen an Facebook, Twitter oder Google in die USA \u00fcbertragen und unter Umst\u00e4nden auch dort gespeichert. N\u00e4heres erfahren Sie durch einen Klick auf das <em>i<\/em>.","settings_perma":"Dauerhaft aktivieren und Daten\u00fcber\u00adtragung zustimmen:","cookie_path":"\/","cookie_expire":"365","cookie_domain":"","css_path":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/socialshareprivacy.css","oben":"nein","overall":"ja","ausschluss_private":"nein","services":{"facebook":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_facebook.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Facebook senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_fb_off":"nicht mit Facebook verbunden","txt_fb_on":"mit Facebook verbunden","display_name":"Facebook","referrer_track":"","language":"de_DE"},"twitter":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_twitter.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Twitter senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_twitter_off":"nicht mit Twitter verbunden","txt_twitter_on":"mit Twitter verbunden","display_name":"Twitter","referrer_track":"","tweet_text":"JPA: Bidirektionale OneToMany\/ManyToOne Beziehungen "},"gplus":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_gplus.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Google+ senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_gplus_off":"nicht mit Google+ verbunden","txt_gplus_on":"mit Google+ verbunden","display_name":"Google+","referrer_track":"","language":"de"}},"uri":"http:\/\/www.icanmakeit.de\/2006\/11\/14\/jpa-bidirektionale-onetomanymanytoone-beziehungen\/"};
				options.cookie_domain = document.location.host;
				$(document).ready(function(){
					$('#socialshareprivacy16').socialSharePrivacy(options);
				});
			})(jQuery);
			</script>
		<!-- Ende von `social share privacy by smeagol.de´ -->
	Tags: <a href="http://www.icanmakeit.de/tag/enterprise/" title="enterprise" rel="tag">enterprise</a><br />
]]></content:encoded>
			<wfw:commentRss>http://www.icanmakeit.de/2006/11/14/jpa-bidirektionale-onetomanymanytoone-beziehungen/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>EJB 3.0: Der steinige Weg zum Komfort</title>
		<link>http://www.icanmakeit.de/2006/10/18/ejb-30-der-steinige-weg-zum-komfort/</link>
		<comments>http://www.icanmakeit.de/2006/10/18/ejb-30-der-steinige-weg-zum-komfort/#comments</comments>
		<pubDate>Wed, 18 Oct 2006 18:27:41 +0000</pubDate>
		<dc:creator>Hendrik Busch</dc:creator>
				<category><![CDATA[Arbeit]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[ejb]]></category>
		<category><![CDATA[enterprise]]></category>

		<guid isPermaLink="false">http://www.icanmakeit.de/2006/10/18/ejb-30-der-steinige-weg-zum-komfort/</guid>
		<description><![CDATA[Eine kleine EinfÃ¼hrung in Stateles SessionBeans mit Jave Enterprise (J5EE) und der neuen EJB 3.0 API. Der Artikel beschreibt auch das Deployment solcher Beans mit dem Glassfish und dem JBoss sowie den Zugriff durch einen Remote-Client.]]></description>
				<content:encoded><![CDATA[<h3>Einleitung</h3>
<p>Bedingt durch meine Arbeit habe ich kürzlich an einer <a title="Übersicht über die Java Enterprise Edition bei Sun" href="http://java.sun.com/javaee/">Java Enterprise</a> Schulung teilgenommen und dort einiges neues gelernt. Der erste Teil der Schulung drehte sich um das Thema <a title="EJB Technologie bei Sun" href="http://java.sun.com/products/ejb/">Enterprise Java Bean</a> (EJB) 3.0. Ich spare mir jetzt mal die Erläuterungen, worum es geht, was man damit tut, usw., denn dieser Beitrag beschäftigt sich vorrangig mit den kleinen Hürden des Alltags.</p>
<p><strong>Die Aufgabe:</strong> Es geht um die vermeintliche einfache Frage, wie man eine simple Bean implementiert, die Abfragen auf eine Datenbank macht und wie man diese Bean von einem Remote-Client aus anspricht. Das war die Aufgabe, die ich mir gestellt hatte, zusammen mit der Aufgabe, diese Software sowohl auf dem <a title="Community des Glassfish Application Servers" href="https://glassfish.dev.java.net/">Glassfish</a> als auch auf dem <a title="Seite des JBoss Application Servers" href="http://labs.jboss.com/portal/jbossas">JBoss</a> zum Laufen zu bringen.</p>
<h3>Entwicklung der Bean</h3>
<p>Vom Prinzip her ist dies der einfachste Teil. Hat man später erstmal die Hürden mit den Application Servern genommen, sollte EJB-Entwicklung eigentlich einfach bleiben, zumindest was den eigentlichen Bean-Teil angeht. Entwickelt werden soll eine <a title="Kapitel über Session Beans im J5EE Tutorial von Sun" href="http://java.sun.com/javaee/5/docs/tutorial/doc/EJBConcepts3.html#wp79775">Stateless SessionBean</a>, die einige unterschiedliche Methoden hat, um Datenbankabfragen zu machen und die Ergebnisse teils als primitiven Typ, teil als Objekt zurück zu geben. Die Bean verwendet eine vom Bean Container bereitgestellte <a title="Beschreibung der DataSource Schnittstelle" href="http://java.sun.com/j2se/1.5.0/docs/api/javax/sql/DataSource.html">DataSource</a> für den Zugriff auf die Datenbank. Diese DataSource wird per <a title="Artikel über Dependency Injection bei OnJava" href="http://www.onjava.com/pub/a/onjava/2006/01/04/dependency-injection-java-ee-5.html">Injection</a> in die Bean geholt.</p>
<h4>Remote Business Interface<strong> </strong></h4>
<p></p><pre class="crayon-plain-tag">package de.icanmakeit.blog.ejb3.client;

import de.icanmakeit.blog.ejb3.model.StatistikData;
import javax.ejb.Remote;
import java.util.Date;

@Remote
public interface StatistikDemo
{
    int getUserCount();
    Date getLastLogin(String username);
    StatistikData getDatasetCount();
}</pre><p></p>
<p>Das Business Interface ist einfach gehalten. Um später von einem entfernten Client darauf zuzugreifen, ist die Annotation <code>@Remote</code> notwendig. Die Klasse <code>StatistikData</code> ist eine POJO-Bean (im Sinne von Java Bean, nicht im Sinne von Enterprise Bean) mit passenden Gettern und Settern. Sie sei hier mal außer Acht gelassen, da sie keine Logik oder spezielle Elemente enthält. Die Klasse implementiert allerdings das Interface <code>Serializable</code>, sonst wäre sie als Datentyp für den entfernten Datenaustausch nicht nutzbar.</p>
<p>Das Business Interface wird von der jeweiligen zuständigen Bean implementiert und dient auch später auf dem Client als Schnittstelle zur Bean.</p>
<h4>Die Stateless SessionBean</h4>
<p>Folgender Code spiegelt die eigentliche Bean wider. Die Logik, die zur Ermittlung der einzelnen Rückgabewerte führt, habe ich der Übersicht halber weggelassen, da sie für das Verständnis der Bean nicht wichtig ist.</p>
<p></p><pre class="crayon-plain-tag">package de.icanmakeit.blog.ejb3.server;

import de.icanmakeit.blog.ejb3.client.StatistikDemo;
import de.icanmakeit.blog.ejb3.model.StatistikData;
import javax.ejb.Stateless;
import javax.sql.DataSource;
import javax.annotation.Resource;
import java.util.Date;

@Stateless(name = &quot;StatistikDemoEJB&quot;, mappedName = &quot;StatistikDemoEJB-MappedName&quot;)
public class StatistikDemoBean implements StatistikDemo
{
    private DataSource _dataSource;

    @Resource(name = &quot;&quot;, mappedName = &quot;&quot;, type = DataSource.class)
    public void setDataSource(final DataSource ds)
    {
        this._dataSource = ds;
    }

    public StatistikDemoBean()
    {
    }

    public int getUserCount()
    {
        final int userCount;
        // Ergebnis bestimmen
        // ...
        return userCount;
    }

    public Date getLastLogin(final String username)
    {
        final Date lastLogin;
        // Ergebnis bestimmen
        // ...
        return lastLogin;
    }

    public StatistikData getDatasetCount()
    {
        final StatistikData result;
        // Ergebnis bestimmen
        // ...
        return result;
    }
}</pre><p></p>
<p>Die Annotation <code>@Stateless</code> signalisiert, dass es sich um eine Stateless SessionBean handelt, die einen Namen und einen MappedName hat. Je nach Application Server wird der eine oder der andere für den späteren Lookup benötigt.</p>
<p>Über die <code>@Resource</code> Annotation wird der Container angewiesen, eine Instanz vom angegebenen Typ durch einen Lookup über den angegebenen Namen oder MappedName zu bestimmen und der Methode als Parameter mit zu geben. Dies geschieht, bevor ein Client irgendeine der definierten Methoden aufruft. Welche Angaben für <code>name</code> und <code>mappedName</code> eingetragen werden müssen, hängt vom jeweiligen Server ab, auf dem die Bean später läuft. Beispiele für JBoss und Glassfish folgen weiter unten.<br />
Die <code>@Resource</code> Annotation könnte auch direkt an der Variable stehen, der Setter könnte dann wegfallen. Allerdings widerspricht das meinem Verständnis von Kapselung, außerdem ist der Getter für einen späteren Test ohne Container notwendig. Es bleibt aber festzuhalten, dass</p>
<p></p><pre class="crayon-plain-tag">private DataSource _dataSource;

@Resource(name = &quot;&quot;, mappedName = &quot;&quot;, type = DataSource.class)
public void setDataSource(final DataSource ds)
{
    this._dataSource = ds;
}</pre><p></p>
<p>und</p>
<p></p><pre class="crayon-plain-tag">@Resource(name = &quot;&quot;, mappedName = &quot;&quot;, type = DataSource.class)
private DataSource _dataSource;</pre><p></p>
<p>den gleichen Effekt haben im EJB 3 Container.</p>
<h4>EJB-Descriptor <code>ejb-jar.xml</code></h4>
<p>EJB 3 lässt dem Entwickler die Wahl, alle wichtigen Einstellungen der Beans auch in einer XML-Datei namens <code>ejb-jar.xml</code> zu definieren. Diese kann zusätzlich zu oder anstelle der Annotations in der Java-Klasse verwendet werden. Im fertigen JAR für das Deployment der EJB ist diese Datei im Verzeichnis <code>META-INF</code> unter zu bringen.<br />
Eine entsprechende Datei für die oben definierte Bean sähe aus wie folgt.</p>
<p></p><pre class="crayon-plain-tag">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;ejb-jar xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot;
         xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
         xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee
		  http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd&quot;
         version=&quot;3.0&quot;&gt;
    &lt;enterprise-beans&gt;
        &lt;session&gt;
            &lt;ejb-name&gt;StatistikDemoEJB&lt;/ejb-name&gt;
            &lt;ejb-class&gt;de.icanmakeit.blog.ejb3.server.StatistikDemoBean&lt;/ejb-class&gt;
            &lt;session-type&gt;Stateless&lt;/session-type&gt;
            &lt;transaction-type&gt;Container&lt;/transaction-type&gt;
            &lt;resource-ref&gt;
                &lt;res-ref-name&gt;&lt;!-- JNDI-Name --&gt;&lt;/res-ref-name&gt;
                &lt;res-type&gt;javax.sql.DataSource&lt;/res-type&gt;
                &lt;injection-target&gt;
                    &lt;injection-target-class&gt;de.icanmakeit.blog.ejb3.server.StatistikDemoBean&lt;/injection-target-class&gt;
                    &lt;injection-target-name&gt;setDataSource&lt;/injection-target-name&gt;
                &lt;/injection-target&gt;
            &lt;/resource-ref&gt;
        &lt;/session&gt;
    &lt;/enterprise-beans&gt;
&lt;/ejb-jar&gt;</pre><p></p>
<h4>Aufruf im Remote-Client</h4>
<p>Grundsätzlich funktioniert folgender Quellcode für jeden Remote-Lookup einer Bean. Allerdings gibt es Unterschiede in Abhängigkeit davon, auf was für einem Server die Bean läuft. Diese Unterschiede machen sich in zwei Punkten bemerkbar:</p>
<ul>
<li>Für die Erzeugung eines InitialContext-Objektes mit dem parameterlosen Konstruktor wird eine Datei jndi.properties im Klassenpfad benötigt, die festlegt, welche Factory den Context mit welcher URL erzeugen soll. Der Inhalt dieser Datei ist server-spezifisch. Details zur Konfiguration folgen in den konkreten Beispielen weiter unten.</li>
<li>Der JNDI-Name der zu verwendenden Bean  variiert ebenfalls je nach Server.</li>
</ul>
<p>Folgender Code verdeutlich aber den grundlegenden Ablauf:</p>
<p></p><pre class="crayon-plain-tag">package de.icanmakeit.blog.ejb.clientapp;

import de.icanmakeit.blog.ejb.client.StatistikDemo;
import de.icanmakeit.blog.ejb.model.StatistikData;
import java.util.Date;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class EJBClient
{
    public static void main(final String[] args) throws NamingException
    {
        final InitialContext ic = new InitialContext();
        final StatistikDemo stat = (StatistikDemo) ic.lookup(&quot;...&quot;);
        System.out.println(&quot;Anzahl Benutzer: &quot; + stat.getUserCount());
        final StatistikData data = stat.getDatasetCount();
        System.out.println(&quot;Anzahl Datens&auml;tze: &quot; + data.getOverallCount() + &quot;, davon &quot; + data.getOutdatedCount() + &quot; nicht akutell.&quot;);
        System.out.println(&quot;Letzer Login des Benutzers 'test': &quot; + stat.getLastLogin(&quot;test&quot;));
    }
}</pre><p></p>
<h3>Deployment im Glassfish</h3>
<p><a title="Community des Glassfish Application Servers" href="https://glassfish.dev.java.net/">Glassfish</a> ist der OpenSource Application Server, den Sun für sich als Sun Application Server 9 verwendet. Er bietet ein komfortables Frontend zur Konfiguration vieler Einstellungen, unter anderem auch für Datenbankverbindungen.</p>
<h4>Datenbank-Verbindung einrichten</h4>
<p>Zunächst muss ein Datenbankpool eingerichtet werden. Dies kann man unter <em>Resources > JDBC > Connection Pools</em> tun. Die Einrichtung sollte selbsterklärend sein. Bitte beachten, dass der Datenbanktreiber dem Server noch im entsprechenden lib-Verzeichnis zugänglich gemacht werden muss. Nachdem der Pool eingerichtet wurde, kann mit ihm eine JDBC-Ressource für den Container bereit gestellt werden. Dies geht im Menüpunkt <em>Resources > JDBC > JDBC Resources</em> und ist ähnlich einfach. Diese Einstellungen fügen Zeilen wie die folgenden in die Datei <code>domain.xml</code> im Verzeichnis <code>domains/<em>config/</em></code>ein:</p>
<p></p><pre class="crayon-plain-tag">&lt;domain&gt;
    ...
    &lt;resources&gt;
        ...
        &lt;jdbc-resource enabled=&quot;true&quot; jndi-name=&quot;jdbc/__testdb&quot; object-type=&quot;user&quot; pool-name=&quot;TestMySQLPool&quot;/&gt;
        ...
        &lt;jdbc-connection-pool 
                allow-non-component-callers=&quot;false&quot; 
                connection-validation-method=&quot;auto-commit&quot; 
                datasource-classname=&quot;com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource&quot;
                fail-all-connections=&quot;false&quot; 
                idle-timeout-in-seconds=&quot;300&quot; 
                is-connection-validation-required=&quot;true&quot; 
                is-isolation-level-guaranteed=&quot;false&quot; 
                max-pool-size=&quot;4&quot; 
                max-wait-time-in-millis=&quot;60000&quot; 
                name=&quot;TestMySQLPool&quot; 
                non-transactional-connections=&quot;false&quot; 
                pool-resize-quantity=&quot;2&quot; 
                res-type=&quot;javax.sql.ConnectionPoolDataSource&quot; 
                steady-pool-size=&quot;1&quot;&gt;
            &lt;description&gt;Testverbindungspool&lt;/description&gt;
            &lt;property name=&quot;user&quot; value=&quot;username&quot;/&gt;
            &lt;property name=&quot;port&quot; value=&quot;3306&quot;/&gt;
            &lt;property name=&quot;password&quot; value=&quot;secret&quot;/&gt;
            &lt;property name=&quot;databaseName&quot; value=&quot;test_db&quot;/&gt;
            &lt;property name=&quot;serverName&quot; value=&quot;myserver.local.net&quot;/&gt;
        &lt;/jdbc-connection-pool&gt;
        ...
    &lt;/resources&gt;
    ...
&lt;/domain&gt;</pre><p></p>
<h4>Bean anpassen &#038; deployen</h4>
<p>Da die DataSource jetzt konfiguriert ist, kann ein entsprechender Name in die <code>@Resource</code> Annotation der Bean eingetragen werden. Für den Glassfish sieht dieser dann aus wie folgt:</p>
<p></p><pre class="crayon-plain-tag">@Resource(name = &quot;jdbc/__testdb&quot;, type = DataSource.class)
public void setDataSource(final DataSource ds)
{
    this._dataSource = ds;
}</pre><p></p>
<p>Diese Regelungen gelten analog für Einträge in die <code>ejb-jar.xml</code>. Das Deployment der Bean kann nun über die Administrationswebseite erfolgen.</p>
<h4>Client anpassen und starten</h4>
<p>Um eine Bean auf dem Glassfish Server anzusprechen, muss auf dem Client ihr <code>mapped name</code> verwendet werden. Der Client wird also wie folgt angepasst:</p>
<p></p><pre class="crayon-plain-tag">final InitialContext ic = new InitialContext();
final StatistikDemo stat = (StatistikDemo) ic.lookup(&quot;StatistikDemoEJB-MappedName&quot;);</pre><p></p>
<p>Der Client läuft nur mit server-spezifischen Klassen im Klassenpfad, es muss die JAR-Datei <code>appserv-rt.jar</code> aus dem lib-Verzeichnis des Servers eingebunden werden. Dies ist leider ein echtes Manko, denn durch fehlende Aufteilung dieser Bibliothek muss jeder Client die vollen 16 MB dieser Datei verwenden. In diese Bibliothek ist auch eine jndi.properties enthalten, deren Einstellungen vollkommen ausreichen, wenn Glassfish auf dem lokalen Rechner läuft. Läuft er woanders, muss eine eigene <code>jndi.properties</code> erstellt und die URL angepasst werden:</p>
<p>[code]<br />
java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory<br />
java.naming.provider.url=iiop://localhost:1050<br />
java.naming.factory.url.pkgs=com.sun.enterprise.naming:com.sun.enterprise.naming<br />
[/code]</p>
<p>Danach sollte einem erfolgreichen Testlauf nichts mehr im Wege stehen.</p>
<h3>Deployment im JBoss</h3>
<p>Um EJB 3.0 mit dem <a title="Seite des JBoss Application Servers" href="http://labs.jboss.com/portal/jbossas">JBoss</a> zu nutzen, muss man den Installer der derzeit aktuellsten Version (4.0.4) herunterladen und den Server darüber installieren. Vollwertige EJB 3 Unterstützung enthält nur das EJB 3.0 Installationsprofil, selbst das Installationsprofil all kann mit EJB 3 nichts anfangen.</p>
<h4>Datenbankverbindung einrichten</h4>
<p>Da der JBoss leider nicht über eine Administrationsoberfläche verfügt, muss man die Datenbank mit Hilfe einer Datei konfigurieren. Zahlreiche Beispiele für unterschiedliche Datenbanken liefert der JBoss direkt mit (Verzeichnis <code>docs/examples/jca</code>). Die Konfigurationsdateien für Datenbankverbindungen müssen auf den Namen <code>-ds.xml</code> enden, damit der JBoss sie als das erkennt, was sie sind. Sie können entweder im Verzeichnis <code>deploy</code> des jeweiligen Profils abgelegt werden (üblicherweise <code>server/default/deploy</code>) oder in die JAR-Datei der Bean integriert werden. Ich benutze für dieses Beispiel eine MySQL-Datenquelle, die wie folgt konfiguriert ist:</p>
<p></p><pre class="crayon-plain-tag">&lt;?xml version=&quot;1.0&quot; encoding=&quot;ISO-8859-1&quot;?&gt;
&lt;!DOCTYPE datasources
        PUBLIC &quot;-//JBoss//DTD JBOSS JCA Config 1.5//EN&quot;
        &quot;http://www.jboss.org/j2ee/dtd/jboss-ds_1_5.dtd&quot;&gt;
&lt;datasources&gt;
    &lt;local-tx-datasource&gt;
        &lt;jndi-name&gt;Test-DS&lt;/jndi-name&gt;
        &lt;connection-url&gt;jdbc:mysql://myserver.local.net/test_db&lt;/connection-url&gt;
        &lt;driver-class&gt;com.mysql.jdbc.Driver&lt;/driver-class&gt;
        &lt;user-name&gt;username&lt;/user-name&gt;
        &lt;password&gt;secret&lt;/password&gt;
        &lt;new-connection-sql&gt;SELECT 1&lt;/new-connection-sql&gt;
        &lt;check-valid-connection-sql&gt;SELECT 1&lt;/check-valid-connection-sql&gt;
        &lt;valid-connection-checker-class-name&gt;
            org.jboss.resource.adapter.jdbc.vendor.MySQLValidConnectionChecker
        &lt;/valid-connection-checker-class-name&gt;
        &lt;exception-sorter-class-name&gt;
            org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter
        &lt;/exception-sorter-class-name&gt;
        &lt;metadata&gt;
            &lt;type-mapping&gt;mySQL&lt;/type-mapping&gt;
        &lt;/metadata&gt;
    &lt;/local-tx-datasource&gt;
&lt;/datasources&gt;</pre><p></p>
<h4>Bean anpassen &#038; deployen</h4>
<p>Die Adressierung der Datenbankverbindung für die Injection in die Bean folgt im JBoss einer anderen Syntax als im Glassfish. Statt der Eigenschaft <code>name</code> muss hier die Eigenschaft <code>mapped name</code> verwendet werden. Hier ist der JNDI-Name der konfigurierten Datenbank mit <code>java:/</code> als Präfix einzusetzen:</p>
<p></p><pre class="crayon-plain-tag">@Resource(mappedName = &quot;java:/Test-DS&quot;, type = DataSource.class)
public void setDataSource(final DataSource ds)
{
    this._dataSource = ds;
}</pre><p></p>
<p>Diese Regelungen gelten analog für Einträge in die <code>ejb-jar.xml</code>. Nachdem man die Bean als JAR gepackt hat, kann sie durch Kopieren dieses JARs in den oben erwähnten Ordner <code>deploy</code> auf dem Server installiert werden. Etwaige Fehler dabei erscheinen in der Logdatei.</p>
<h4>Client anpassen und starten</h4>
<p>Um auf die im JBoss laufende Bean entfernt zugreifen zu können, muss im Client der Lookup über den Namen der Bean mit dem Zusatz /remote erfolgen:</p>
<p></p><pre class="crayon-plain-tag">final InitialContext ic = new InitialContext();
final StatistikDemo stat = (StatistikDemo) ic.lookup(&quot;java:/StatistikDemoEJB/remote&quot;);</pre><p></p>
<p>Damit der Lookup klappt, muss auch eine JBoss-spezifische Konfiguration in der <code>jndi.properties</code> eingetragen werden. Im Falle des JBoss gibt es keine Vorgabedatei in einer der Bibliotheken, es muss also immer eine eigene angelegt werden. Es ist hierbei darauf zu achten, dass die URL nicht wie beim Glassfish mit einem Protokoll versehen wird. Die Eintragung von z.B. <code>iiop://</code> würde zu einer Exception <em>Unknown protocol iiop</em> führen.</p>
<p>[code]<br />
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory<br />
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces<br />
java.naming.provider.url=localhost:1099<br />
[/code]</p>
<p>Im Gegensatz zum Glassfish hat der JBoss sauber getrennte Client-Bibliotheken, die schon fast zu speziell unterteilt sind. Für eine Anwendung wie diese müssen folgende Bibliotheken aus dem client-Verzeichnis verwendet werden:</p>
<ul>
<li><code>concurrent.jar</code></li>
<li><code>jboss-aop-jdk50-client.jar</code></li>
<li><code>jboss-aspect-jdk50-client.jar</code></li>
<li><code>jboss-client.jar</code></li>
<li><code>jboss-common-client.jar</code></li>
<li><code>jboss-ejb3-client.jar</code></li>
<li><code>jboss-j2ee.jar</code></li>
<li><code>jboss-remoting.jar</code></li>
<li><code>jbosssx-client.jar</code></li>
<li><code>jboss-transaction-client.jar</code></li>
<li><code>jnp-client.jar</code></li>
</ul>
<p>Für andere Clients könnten noch zusätzliche Bibliotheken notwendig werden. Ein Teil der Bibliotheken ist auch zusammengefasst im <code>jbossall-client.jar</code>, da diese Bibliothek nicht ausreicht und alleine schon 4 MB auf die Waage bringt, hielt ich es für sinnvoller, die einzelnen Dateien aufzulisten, diese zusammen sind nur 2 MB groß (also um Faktor 8 kleiner als beim Glassfish).</p>
<h3>Fazit</h3>
<p>Der eigentlich EJB 3.0 Standard stellt für Enterprise-Entwickler eine deutliche Erleichterung gegenüber EJB 2.0 dar. Für Neulinge in dem Bereich wird das Erlernen der Techniken einfacher, weil komplizierte und abstrakte Konstrukte abgeschafft wurden.</p>
<p>Gemessen an den anfänglichen Hürden zwischen der EJB-Theorie und dem tatsächlichen praktischen Einsatz, wäre es besser gewesen, man hätte gerade im Bereich JNDI-Adressierung für die am häufigsten verwendeten Dinge wie Datenbankverbindungen, EntityManager, o.ä. ein einheitliches Adressierungssystem mit in den Standard aufgenommen. Die Einarbeitung in die unterschiedlichen Implementierungen gleicht gerade zu Beginn einem Puzzlespiel. Hat man diese Hürden aber erstmal genommen, steht dem komfortablen Einsatz von EJB 3.0 nichts mehr im Wege (zumindest, bis man auf die nächste &#8220;Tretmine&#8221; tritt).</p>
<!-- Beginn von `social share privacy by smeagol.de´ --><div id="socialshareprivacy17"></div>
			<script type="text/javascript">
			(function($){
				var options = {"info_link":"http:\/\/heise.de\/-1333879","txt_help":"Wenn Sie diese Felder durch einen Klick aktivieren, werden Informationen an Facebook, Twitter oder Google in die USA \u00fcbertragen und unter Umst\u00e4nden auch dort gespeichert. N\u00e4heres erfahren Sie durch einen Klick auf das <em>i<\/em>.","settings_perma":"Dauerhaft aktivieren und Daten\u00fcber\u00adtragung zustimmen:","cookie_path":"\/","cookie_expire":"365","cookie_domain":"","css_path":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/socialshareprivacy.css","oben":"nein","overall":"ja","ausschluss_private":"nein","services":{"facebook":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_facebook.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Facebook senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_fb_off":"nicht mit Facebook verbunden","txt_fb_on":"mit Facebook verbunden","display_name":"Facebook","referrer_track":"","language":"de_DE"},"twitter":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_twitter.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Twitter senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_twitter_off":"nicht mit Twitter verbunden","txt_twitter_on":"mit Twitter verbunden","display_name":"Twitter","referrer_track":"","tweet_text":"EJB 3.0: Der steinige Weg zum Komfort "},"gplus":{"status":"on","dummy_img":"http:\/\/www.icanmakeit.de\/wp-content\/plugins\/2-klicks-button-socialshareprivacy-plugin\/images\/dummy_gplus.png","txt_info":"2 Klicks f\u00fcr mehr Datenschutz: Erst wenn Sie hier klicken, wird der Button aktiv und Sie k\u00f6nnen Ihre Empfehlung an Google+ senden. Schon beim Aktivieren werden Daten an Dritte \u00fcbertragen \u2013 siehe <em>i<\/em>.","txt_gplus_off":"nicht mit Google+ verbunden","txt_gplus_on":"mit Google+ verbunden","display_name":"Google+","referrer_track":"","language":"de"}},"uri":"http:\/\/www.icanmakeit.de\/2006\/10\/18\/ejb-30-der-steinige-weg-zum-komfort\/"};
				options.cookie_domain = document.location.host;
				$(document).ready(function(){
					$('#socialshareprivacy17').socialSharePrivacy(options);
				});
			})(jQuery);
			</script>
		<!-- Ende von `social share privacy by smeagol.de´ -->
	Tags: <a href="http://www.icanmakeit.de/tag/ejb/" title="ejb" rel="tag">ejb</a>, <a href="http://www.icanmakeit.de/tag/enterprise/" title="enterprise" rel="tag">enterprise</a><br />
]]></content:encoded>
			<wfw:commentRss>http://www.icanmakeit.de/2006/10/18/ejb-30-der-steinige-weg-zum-komfort/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
