Memakai Ulang Props Di Definisi XML Spring


Sejak Spring versi 3.1, terdapat fungsi profiles dimana definisi bean tertentu bisa diatur agar hanya dikerjakan (dianggap ada) bila kondisi tertentu dipenuhi.  Kondisi yang dimaksud disini adalah nama profile harus sesuai.   Nama profile bisa ditentukan melalui VM arguments saat menjalankan server atau melalui tag <param-name> di <context-param> web.xml.  Cukup berikan parameter spring.profiles.active dengan nilai apa saja yang mewakili nama profile.  Btw, nama profile juga bisa ditentukan melalui annotation @ActiveProfiles bila dipakai dalam pengujian melalui SpringJUnit4ClassRunner.class.

Saya sering memakai fitur profile untuk membedakan bean untuk pengujian dan produksi.  Misalnya, pada pengujian, saya akan mendefinisikan database H2 (embedded)  sementara pada produksi, saya akan memakai database MySQL.  Selain itu, saya juga dapat mengatur agar Hibernate JPA  secara otomatis create-drop tabel hanya pada profile pengujian.

Tapi penggunaan profile menimbulkan sebuah permasalahan baru yang berkaitan dengan copy paste.  Sebagai contoh berikut ini adalah potongan dari definisi XML untuk bean saya:

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="jpaVendorAdapter">
    <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
  </property>
  <property name="packagesToScan" value="co.id.jocki.latihan.domain" />
  <property name="jpaProperties" ref="jpaProperties" />
</bean>

<beans profile="dev,production">

  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/latihan" />
    <property name="username" value="user" />
    <property name="password" value="skripsi" />
  </bean>

  <bean id="jpaProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="properties">
      <props>
        <prop key="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
        <prop key="hibernate.max_fetch_depth">3</prop>
        <prop key="hibernate.jdbc.fetch_size">50</prop>
        <prop key="hibernate.jdbc.batch_size">10</prop>
        <prop key="hibernate.id.new_generator_mappings">true</prop>
        <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
      </props>
    </property>
  </bean>

</beans>

<beans profile="test">

  <jdbc:embedded-database id="dataSource" type="H2" />

  <bean id="jpaProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="properties">
      <props>
        <prop key="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
        <prop key="hibernate.max_fetch_depth">3</prop>
        <prop key="hibernate.jdbc.fetch_size">50</prop>
        <prop key="hibernate.jdbc.batch_size">10</prop>
        <prop key="hibernate.hbm2dll.auto">create-drop</prop>
        <prop key="hibernate.id.new_generator_mappings">true</prop>
        <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
      </props>
    </property>
  </bean>

</beans>

<beans profile="selenium-test">

  <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/testdatabase" />

  <bean id="jpaProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="properties">
      <props>
        <prop key="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
        <prop key="hibernate.max_fetch_depth">3</prop>
        <prop key="hibernate.jdbc.fetch_size">50</prop>
        <prop key="hibernate.jdbc.batch_size">10</prop>
        <prop key="hibernate.hbm2dll.auto">create-drop</prop>
        <prop key="hibernate.id.new_generator_mappings">true</prop>
        <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
      </props>
    </property>
  </bean>

</beans>

Pada definisi bean di atas, bean jpaProperties berisi properties untuk Hibernate.  Pada profile dev dan production, saya memakai MySQL sehingga nilai hibernate.dialect adalah org.hibernate.dialect.MySQLDialect.  Saya tidak bisa memakai ulang bean ini di profile test dan selenium-test, karena pada profile tersebut saya memakai database H2 sehingga nilai hibernate.dialect seharusnya adalah org.hibernate.dialect.H2Dialect.

Permasalahan akan timbul bila suatu saat nanti saya merubah nilai jpaProperties.  Mungkin saya mengubah nilai baru, atau menghapus.  Seandainya saya terburu-buru dan sedang dilanda pusing (karena developer sangat sering coding dalam keadaan seolah-olah dikejar binatang buas), maka mungkin sekali saya LUPA kalau saya harus mengubah jpaProperties di TIGA tempat yang berbeda!

Ok, karena sekarang saya sedang santai, saya harus mempersiapkan perisai untuk mengurangi kemungkinan hal tidak diinginkan di masa depan.

Nilai-nilai yang umum dari ketiga bean jpaProperties tersebut dapat saya taruh ke sebuah bean tersendiri sebagai parent.  Dengan demikian, pada saat saya akan melakukan perubahan secara umum, saya hanya perlu mengubah satu bean parent  ini.  Berikut ini adalah definisi bean tersebut:

<bean id="parentJpaProperties" abstract="true" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
  <property name="properties">
    <props>
       <prop key="hibernate.max_fetch_depth">3</prop>
       <prop key="hibernate.jdbc.fetch_size">50</prop>
       <prop key="hibernate.jdbc.batch_size">10</prop>
       <prop key="hibernate.id.new_generator_mappings">true</prop>
       <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
    </props>
  </property>
</bean>

Yang perlu diperhatikan adalah saya menambahkan abstract=”true” pada definisi bean parent tersebut.  Ini berarti bean parentJpaProperties tidak dapat dipakai secara langsung, melainkan harus melalui child-nya.

Pada profile untuk dev dan production, saya bisa membuat bean jpaProperties seperti berikut ini:

<bean id="jpaProperties" parent="parentJpaProperties">
  <property name="properties">
    <props merge="true">
      <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
    </props>
  </property>
</bean>

Pada bean ini saya mendefinisikan parent=”parentJpaProperties” sehingga nilai dari bean parentJpaProperties bisa dipakai ulang.  Saya menambahkan merge=”true” sehingga props yang ada disini akan digabungkan dengan milik parent, bukannya menimpa dan menggantikan milik parent.

Pada profile test dan selenium-test, saya tinggal membuat bean dengan nama yang sama, tetapi isi props yang berbeda, seperti berikut ini:

<bean id="jpaProperties" parent="parentJpaProperties">
  <property name="properties">
    <props merge="true">
      <prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop>
      <prop key="hibernate.hbm2dll.auto">create-drop</prop>
    </props>
  </property>
</bean>

Perihal Solid Snake
I'm nothing...

One Response to Memakai Ulang Props Di Definisi XML Spring

  1. Ping-balik: Melakukan Front-End Testing « The Solid Snake

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: