Konfiguration eines Spring Data Projektes

Ein neues Projekt schrie förmlich nach dem Einsatz von Spring Data, da eine Repository-Schicht benötigt wurde, die möglichst schnell erstellt werden sollte. Die Entites existierten bereits und so mussten nur die Zugriffsfunktionen noch geschrieben werden. Um mit Spring Data vertraut zu werden, galt es ein Beispielprojekt aufzusetzen. Im Folgenden möchte ich meine Erfahrungen bei der Konfiguration des Projektes beschreiben.

Dependencies

Zuerst habe ich ein Maven-Projekt in Eclipse angelegt und mich im Internet umgesehen, welche Dependencies für Spring Data benötigt werden. Hier die Auswahl, die für meine Bedürfnisse passte:

<dependencies>
	<!-- Spring Data -->
	<dependency>
		<groupId>org.springframework.data</groupId>
		<artifactId>spring-data-jpa</artifactId>
		<version>1.5.0.M1</version>
	</dependency>

	<!-- Hibernate -->
	<dependency>
		<groupId>org.hibernate</groupId>
		<artifactId>hibernate-entitymanager</artifactId>
		<version>4.3.0.Final</version>
		<scope>runtime</scope>
	</dependency>
	
	<!-- H2 -->
	<dependency>
		<groupId>com.h2database</groupId>
		<artifactId>h2</artifactId>
		<version>1.3.175</version>
	</dependency>
	
	<!-- Logging -->
	<dependency>
		<groupId>log4j</groupId>
		<artifactId>log4j</artifactId>
		<version>1.2.17</version>
	</dependency>
	<dependency>
		<groupId>org.slf4j</groupId>
		<artifactId>slf4j-api</artifactId>
		<version>1.7.1</version>
	</dependency>
	<dependency>
		<groupId>org.slf4j</groupId>
		<artifactId>slf4j-log4j12</artifactId>
		<version>1.7.5</version>
	</dependency>
	
	<!-- Test -->
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.11</version>
		<exclusions>
			<exclusion>
				<groupId>org.hamcrest</groupId>
				<artifactId>hamcrest-core</artifactId>
			</exclusion>
		</exclusions>
	</dependency>
	<dependency>
		<groupId>org.hamcrest</groupId>
		<artifactId>hamcrest-all</artifactId>
		<version>1.3</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-test</artifactId>
		<version>${spring.version}</version>
	</dependency>
</dependencies>

Einige Anmerkungen hierzu:

  • Spring-Data-Version: ich habe den aktuellen Milestone 1.5.0.M1 verwendet, da dieser auf eine aktuellere Spring-Version (3.2.5.RELEASE) aufsetzt. Hierfür muss allerdings das entsprechende Springframework-Repository angegeben werden:
    
    	
    		springframework
    		http://maven.springframework.org/milestone
    	
    
    
  • Unit-Tests: obwohl ich gerne JUnit einsetze, neige ich in letzter Zeit dazu, Hamcrest intensiver zu verwenden. Hierzu ist es notwendig, das in JUnit enthaltene Hamcrest 1.3 auszuschalten (exclusion) und dafür Hamcrest 1.3-all einzubinden. Auf diese Art lassen sich erst die vielen statischen Matcher, die in org.hamcrest.Matchers enthalten sind, nutzen.
  • Datenbank: Als Datenbank setze ich gerne H2 ein, da diese sowohl für Unit-Test in einer Embedded-Version als auch für produktive Einsätze als Server-Version zur Verfügung steht.

Spring Konfiguration

Meiner Meinung nach sollte eine moderne Spring-Applikation eine Konfiguration im Java-Config-Style einsetzen, d.h. mit Annotations anstelle von XML-Dateien arbeiten. Es gibt hierfür sicherlich sowohl Vor- als auch Nachteile, aber für mich überwiegen die Vorteile.

Die von mir verwendete Konfiguration sieht wie folgt aus:

@Configuration
@EnableJpaRepositories
@EnableTransactionManagement
public class ApplicationConfig {

  @Bean
  public DataSource dataSource() {
    EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
    return builder
        .setType(EmbeddedDatabaseType.H2)
        .addScript("classpath:create.sql")
        .addScript("classpath:import.sql")
        .build();
  }

  @Bean
  public EntityManagerFactory entityManagerFactory() {
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setDatabase(Database.H2);
    vendorAdapter.setGenerateDdl(true);

    LocalContainerEntityManagerFactoryBean factory =
        new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setDataSource(dataSource());
    factory.setPackagesToScan(getClass().getPackage().getName());
    factory.afterPropertiesSet();

    return factory.getObject();
  }

  @Bean
  public PlatformTransactionManager transactionManager() {
    JpaTransactionManager txManager = new JpaTransactionManager();
    txManager.setEntityManagerFactory(entityManagerFactory());
    return txManager;
  }

  @Bean
  public HibernateExceptionTranslator hibernateExceptionTranslator() {
    return new HibernateExceptionTranslator();
  }

Auch auf diesen Code möchte ich mit einigen Anmerkungen eingehen:

  • Data Source: Spring JDBC stellt einen EmbeddedDatabaseBuilder zur Verfügung, der für die Datenbanken H2, HSQL und DERBY eine vereinfachte Konfiguration anbietet. Insbesondere können Datenbankskripte für die Erzeugung und Befüllung der benötigiten Tabellen angegeben werden, so dass z.B. Testfälle auf vorkonfigurierten Datenbanken aufsetzen können.
  • LocalContainerEntityManagerFactoryBean: Spring stellt hiermit eine einfache und mächtige Möglichkeit zur Verfügung, eine EntityManagerFactory zu initialisieren. Für die Konfiguration wird zum einen ein HibernateJpaVendorAdapter benötigt, der die Einstellungen für Hibernate vornimmt, z.B. die Angabe der Datenbank und ob die DDL erzeugt werden soll. Zum anderen muss natürlich die bereits als Bean definierte DataSource angegeben werden.
    Wichtig: am Ende der Konfiguration sollte (muss?) afterPropertiesSet(); aufgerufen werden. Sonst traten bei mir in einigen Konfigurationsfällen seltsame Fehlermeldungen auf (dass Beans nicht initialisiert werden konnten) ohne dass eine klare Ursache erkennbar war.
  • HibernateExceptionTranslator: Treten im Hibernate-Code Exceptions auf, die nicht in die HibernateException-Hierarchie fallen, wie z.B. IllegalStateException, so müssen diese übersetzt werden. In Hibernate 3 geschah dies noch automatisch, da ich aber Hibernate 4 verwende, muss die Bean konfiguriert werden.
  • EnableJpaRepositories: Meine Konfiugurationsklasse befindet sich im Root-Package, deshalb werden alle darunter liegenden Repositories durch diese Annotation erkannt. Liegt die Annotation-Konfiguration aber an anderer Stelle, muss der Parameter basePackages (oder sein typsicheres Pendant basePackageClasses) verwendet werden.

JUnit-Tests

Die Testfälle lassen sich nur sehr leicht erstellen, indem die Testklasse mit

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TestConfig.class })

annotiert wird.

Jetzt mussten nur noch die Testfälle geschrieben werden und neue Funktionen in die Spring-Data-Repositories eingefügt werden. Hierfür gibt es aber bereits genügend Artikel im Internet. Empfehlenswert ist z.B. Sprint Data Repositories — A deep dive.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.