Archiv der Kategorie: Spring

@ManagedResource in Spring is really cool

First, write a class and annotate it:

package com.javatutor.spring.jmx;

import java.util.HashMap;

import java.util.Map;

import org.springframework.jmx.export.annotation.ManagedAttribute;

import org.springframework.jmx.export.annotation.ManagedOperation;

import org.springframework.jmx.export.annotation.ManagedResource;

@ManagedResource( objectName = "com.javatutor.spring.jmx:name=StringMap" )

public class StringMapImpl

{

private Map<String, String> map = new HashMap<String, String>();

@ManagedAttribute

public int getSize()

{

return map.size();

}

@ManagedOperation

public String get( String key )

{

return map.get( key );

}

@ManagedOperation

public String put( String key, String value )

{

return map.put( key, value );

}

public void remove( String key )

{

map.remove( key );

}

}

Next, write a spring config file:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

<bean id="StringMap" class="com.javatutor.spring.jmx.StringMapImpl" />

<bean id="MBeanExporter"

class="org.springframework.jmx.export.MBeanExporter">

<property name="assembler" ref="MBeanInfoAssembler" />

<property name="autodetect" value="true" />

<property name="namingStrategy" ref="NamingStrategy" />

</bean>

<bean id="AnnotationJmxAttributeSource"

class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"

/>

<bean id="NamingStrategy"

class="org.springframework.jmx.export.naming.MetadataNamingStrategy">

<property name="attributeSource" ref="AnnotationJmxAttributeSource" />

</bean>

<bean id="MBeanInfoAssembler"

class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">

<property name="attributeSource" ref="AnnotationJmxAttributeSource" />

</bean>

</beans>

Now the client:

package com.javatutor;

import javax.swing.JOptionPane;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.javatutor.spring.jmx.StringMapImpl;

public class SpringWithJmxExample

{

public static void main( String[] args )

{

ApplicationContext beFac = new ClassPathXmlApplicationContext( "springJmx.xml"

);

StringMapImpl cacheBean = (StringMapImpl) beFac.getBean( "StringMap" );

cacheBean.put( "Key", "Value" );

System.out.println( cacheBean.get("Key") );

JOptionPane.showMessageDialog( null, "End" );

}

}

Start the program. Do not forgett to start with -Dcom.sun.management.jmxremote.
Start JConsole and enjoy.

 

Spring, Hibernate und HSQLDB

Nach dem das erste Beispiel das Zusammenspiel von Spring und HSQLDB zeigte, soll das zweite Beispiel statt dem JdbcTemplate das HibernateTemplate nutzen, damit Hibernate die OR-Abbildung übernimmt. Zur Vorbereitung (und Erweiterung) des ersten Beispiels wird zunächst log4j.properties um eine Zeile erweitert:

log4j.rootCategory=ERROR, A1
log4j.logger.net.sf.ehcache=ERROR, A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%m%n

Für Hibernate sind ebenso weitere Jar-Dateien nötig, so dass sich insgesamt
im Pfad befinden müssen:

  • hsqldb.jar
  • log4j-*.jar
  • spring.jar
  • common-logging.jar
  • hibernate3.jar
  • dom4j-*.jar
  • jta.jar
  • common-collections-*.jar
  • ehcache-*.jar
  • cglib-*.jar
  • asm.jar
  • antlr-*.jar

Als erstes zeigt die DAO-Implementierung die Verwendung von HibernateTemplate.

package com.javatutor.dao.hibernate;

import java.util.Collection;

import org.springframework.orm.hibernate3.HibernateTemplate;

import com.javatutor.dao.CustomerDao;
import com.javatutor.domain.Customer;

public class CustomerDaoHibernateImpl extends HibernateTemplate implements CustomerDao
{
@SuppressWarnings("unchecked")
public Collection<Customer> getCustomers()
{
return loadAll( Customer.class );
}

public Customer findCustomerById( int id )
{
return (Customer) load( Customer.class, id );
}

public void save( Customer customer )
{
saveOrUpdate( customer );
}
}

Zum Mappen der POJOs auf die Datenbank ist eine Mapping-Datei nötig, es sei denn, man arbeitet mit Java 5 Annotationen. In den Klassenpfad kommt die Datei Customer.hbm.xml.

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.javatutor.domain">

<class name="Customer" table="Customers" lazy="false">

<id name="id" column="Id">
<generator class="native" />
</id>

<property name="name" column="Name" />

</class>

</hibernate-mapping>

In der Spring XML-Datei müssen wir den CustomerDaoHibernateImpl anmelden und
mit einer SessionFactory versorgen.

<bean id="CustomerDaoHibernate" class="com.javatutor.dao.hibernate.CustomerDaoHibernateImpl">
<property name="sessionFactory">
<ref local="SessionFactory" />
</property>
</bean>

Die SessionFactory ist für Hibernate nötig und enthält den Hinweis auf die OR-Mappings und die DataSource. Die DataSource haben wir schon verwendet, doch damit alles persistent bleibt, soll HSQLDB diesmal ins Dateisystem schreiben.

<bean id="DataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>org.hsqldb.jdbcDriver</value>
</property>
<property name="url">
<value>jdbc:hsqldb:file:/c/:Customers;shutdown=true</value>
</property>
<property name="username">
<value>sa</value>
</property>
<property name="password">
<value></value>
</property>
</bean>

Es ist sehr wichtig, den Parameter ;shutdown=true an die Verbindungs-URL zu setzen. Die LocalSessionFactoryBean ist der Produzent für unsere SessionFactory:

<bean id="SessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="DataSource" />
</property>
<property name="mappingResources">
<list>
<value>Customer.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.HSQLDialect
</prop>
<!-- <prop key="hibernate.show_sql">true</prop>-->
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>

Mit dem hibernate.hbm2ddl.auto ist dann auch in der Applikation das CREATE TABLE unnötig. Und das war’s dann auch schon.

Mit Spring und dem JdbcTemplate auf eine Hibernate-Datenbank

Das folgende Beispiel soll zeigen, wie man mit Spring arbeitet und Daten in einer relationalen Datenbank persistent macht. Um das Bespiel zum Laufen zu bringen müssen im Klassenpfad sein: hsqldb.jar, spring.jar, log4j-1.2.9.jar und common-logging.jar. Für die Log-Meldungen setzen wir in den Klassenpfad die Datei log4j.properties:

log4j.rootCategory=WARN, A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%m%n

Beginnen wir mit unserem Geschäftsobjekt, einem Kunden:

package com.javatutor.domain;

public class Customer
{
private int id;

private String name;

public int getId()
{
return id;
}

public void setId( int id )
{
this.id = id;
}

public String getName()
{
return name;
}

public void setName( String name )
{
this.name = name;
}

@Override
public String toString()
{
return String.format( "Customer[id=%d, name=%s]", id, name );
}
}

Für diesen Kunden definieren wir eine DAO-Schnittstelle, die uns später Exemplare der Geschäftsobjekte gibt und die Speicherung ermöglichen.

package com.javatutor.dao;

import java.util.Collection;

import com.javatutor.domain.Customer;

public interface CustomerDao
{
Collection<Customer> getCustomers();

Customer findCustomerById( int id );

void save( Customer customer );
}

Zum Testen der Applikation beginnen wir mit einer einfach DAO-Implementierung, die Kunden in einer HashMap speichert.


package com.javatutor.dao.map;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import com.javatutor.dao.CustomerDao;
import com.javatutor.domain.Customer;

public class CustomerDaoMapImpl implements CustomerDao
{
private Map<Integer, Customer> map = new HashMap<Integer, Customer>();

public Collection<Customer> getCustomers()
{
return map.values();
}

public Customer findCustomerById( int id )
{
return map.get( id );
}

public void save( Customer customer )
{
map.put( customer.getId(), customer );
}
}

Eine Applikation wird über die Spring-Konfigurationsdatei später mit einem konkreten CustomerDao gespritzt. Die Methode haveFun() legt einige Business-Objekte an und speichert sie über das DAO.


package com.javatutor;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

import com.javatutor.dao.CustomerDao;
import com.javatutor.domain.Customer;

public class Application
{
private CustomerDao customerDao;

public void setCustomerDao( CustomerDao customerDao )
{
this.customerDao = customerDao;
}

private void haveFun()
{
System.out.println( customerDao.getCustomers() );

Customer c1 = new Customer();
c1.setId( 0 );
c1.setName( "Christian Ullenboom" );
customerDao.save( c1 );

System.out.println( customerDao.findCustomerById( 0 ) );

System.out.println( customerDao.getCustomers() );

Customer c2 = new Customer();
c2.setId( 1 );
c2.setName( "Tantiana Roujitcher" );
customerDao.save( c2 );

System.out.println( customerDao.getCustomers() );
}

//

public static void main( String[] args )
{
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "spring.xml" );
Application bean = (Application) context.getBean( "Application" );
bean.haveFun();
}
}

Jetzt fehlt nur noch die XML-Datei für Spring:


<?xml version="1.0"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
<bean id="Application" class="com.javatutor.Application">
<property name="customerDao">
<ref local="CustomerDao" />
</property>
</bean>
<bean id="CustomerDao" class="com.javatutor.dao.map.CustomerDaoMapImpl" />
</beans>

Startet man das Programm, ist die Ausgabe


[]
Customer[id=0, name=Christian Ullenboom]
[Customer[id=0, name=Christian Ullenboom]]
[Customer[id=1, name=Tantiana Roujitcher], Customer[id=0, name=Christian Ullenboom]]

Prima. Es klappt. Jetzt kommt eine DAO-Implementierung für JDBC. Zunächst die Klasse.


package com.javatutor.dao.jdbc;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;

import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import com.javatutor.dao.CustomerDao;
import com.javatutor.domain.Customer;

public class CustomerDaoJdbcImpl extends JdbcTemplate implements CustomerDao
{
@SuppressWarnings("unchecked")
public Collection<Customer> getCustomers()
{
return query( "SELECT Id, Name FROM Customers", new CustomerRowMapper() );
}

public Customer findCustomerById( int id )
{
String sql = "SELECT Id, Name FROM Customers WHERE Id = ?";

try
{
return (Customer) queryForObject( sql,
new Object[] { id },
new CustomerRowMapper());
}
catch ( IncorrectResultSizeDataAccessException e ) { }

return null;
}

public void save( Customer customer )
{
if ( findCustomerById( customer.getId() ) == null )
{
Object[] args = { customer.getId(), customer.getName() };
update( "INSERT INTO Customers (Id, Name) VALUES (?,?)", args );
}
else
{
Object[] args = { customer.getName(), customer.getId() };
update( "UPDATE Customers SET Name = ? WHERE Id = ?", args );
}
}
}

class CustomerRowMapper implements RowMapper
{
public Object mapRow( ResultSet rs, int rowNum ) throws SQLException
{
Customer c = new Customer();
c.setId( rs.getInt( "Id" ) );
c.setName( rs.getString( "Name" ) );
return c;
}
}

Und in der XML-Datei ergänzen wir für den JDBC-DAO:

<bean id="CustomerDaoJdbc" class="com.javatutor.dao.jdbc.CustomerDaoJdbcImpl">
<property name="dataSource"><ref local="DataSource" /></property>
</bean>

<bean id="DataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>org.hsqldb.jdbcDriver</value>
</property>
<property name="url">
<value>jdbc:hsqldb:mem:customers</value>
</property>
<property name="username">
<value>sa</value>
</property>
<property name="password">
<value></value>
</property>
</bean>

Damit unsere Applikation mit dem neuen JDBC-DAO gespritzt wird setzt man.

 <bean id="Application" class="com.javatutor.Application">
<property name="customerDao">
<ref local="CustomerDaoJdbc" />
</property>
<property name="dataSource">
<ref local="DataSource" />
</property>
</bean>

Gleichzeitig geben wir der Applikation eine DataSource, damit sie die Tabelle für die Datenbank anlegen kann.


public void setDataSource( DataSource dataSource )
{
new JdbcTemplate( dataSource ).execute( "CREATE TABLE Customers( Id INTEGER, Name VARCHAR(128) ) " );
}

Das gestartet Programm gibt nun die gleiche Ausgabe.