Tag Archives: Spring

Skrytý drahokam Springu

Dnes jsem koukal na rozhovor s Rodem Johnsonem, v kterém mluvil o novinkách ve Springu 3.0. Mezi řečí se zmínil o anotaci @Configurable, která je už ve Springu 2.5 a označil ji za skrytý drahokam. Protože jsem o něčem takovém slyšel poprvé, tak jsem se podíval do dokumentace a docela mě to zaujalo. (Teda, teď koukám, že jsem se o něčem podobném zmiňoval už před půl rokem zde, ach ta paměť). V zásadě jde o to, že obvykle funguje dependency injection jen u bean, které spravuje Spring. Takže například nejde použít u JPA entit a podobně. Což je docela škoda, protože pak musíme dělat umělou servisní vrstvu a objektový model tím docela trpí. Máme na jednu stranu třídy, které drží stav (entity) a na druhou stranu servisní třídy, v kterých je jen kód a stavu mají pomálu. Ale ve správném objektovém programování bychom měli mít třídy, které kombinují oboje. Takže například objekt User by měl mít metodu remove(), která by ho smazala z databáze. Jenže to by znamenalo, že by každá instance této třídy musela mít v sobě odkaz na EntityManager nebo ještě lépe DAO, což neumíme injektnout.

Možná si říkáte, že jsem se zbláznil, že to je ošklivé. Musím se přiznat, že mi je tento přístup taky cizí, ale asi je to jen otázka zvyku. Nejspíš jsem jen zvyklý na to procedurální programování, kde mám servisní procedury v singletonu a jim předhazuji hloupé datové objekty, které se o sebe neumějí postarat samy.

Anotace @Configurable je tu právě od toho, aby nám usnadnila injektování i do bean, které nejsou spravovány Springem. Předvedu na příkladu. Mám třídu Bean, které se dá podstrčit nějaký text.

@Configurable
public class Bean {
	private String text;
	public String getText() {
		return text;
	}
	public void setText(String text) {
		this.text = text;
	}
}

Mým cílem je zprovoznit tento test.

	public void testIt()
	{
		Bean bean = new Bean();
		assertEquals("Hallo",bean.getText());
	}

Všimněte si, že vytvářím novou instanci a koukám se, co je v ní nastaveno za text. Podle všeho by tam mělo být null. Ale pokud správně poladím Spring, tak tam může být cokoliv.

Prvním krokem je samozřejmě anotace @Configurable, která označí beanu jako kandidáta na injektování. V druhém kroku musím nastavit, co chci injektnout. Mohu například zvolit klasickou XML konfiguraci.

	<bean class="net.krecan.configurable.Bean" scope="prototype">
		<property name="text" value="Hallo"/>
	</bean>

	<context:spring-configured/>
	
	<context:load-time-weaver/

Zde si všimněme několika věcí. Například toho, že Spring pozná to kam má co injektnout automaticky, podle jména třídy. Nicméně dají se použít i vlastní jména. Pak si všimněme použití rozsahu „protoype“. To dává smysl, já mohu new zavolat kolikrát chci (i když tam to protoype nedáte, tak se to chová stejně, takhle je to jen hezčí). No a pak už nám zbývá jen magie. Pomocí prvního kouzelného slůvka <context:spring-configured/> zařídíme, aby se použil AspectJ aspekt, který to všechno zařídí. Ano, musí se použít AspectJ, zázraky ani Spring nedovede. Zatím.

Druhé kouzelné slůvko <context:load-time-weaver/> nastaví AspectJ, tak aby si podle potřeby při startu aplikace upravil třídy. Aby to totiž všechno fungovalo, tak je potřeba upravit bytecode. A to buď při kompilaci nebo při startu aplikace. Já jsem zvolil druhou variantu, která bohužel také vyžaduje, abyste to celé spouštěli s atributem virtuálního stroje -javaagent:spring-agent-2.5.6.jar. Pokud si kód budete zkoušet sami, tak na to prosím nezapomeňte.

A to je vše, testy projdou bez problému. Vidíme, že Spring umí injektovat i do tříd, nad kterými na první pohled nemá kontrolu. A to aniž bychom nějak zvlášť museli študovat AspectJ. Všechno už máme pěkně připraveno. Samozřejmě, uvedený příklad je umělý, ale pokud bychom našli dost odvahy, tak můžeme bez problémů injektnout EntityManager přímo do entit. A to může být docela užitečné.

Zdrojový kód je ke stažení zde, obzvláště doporučuji extra zvrhlou třídu MagicBean. No a pokud chcete umět Spring tak skvěle jako já, tak se mi ozvěte, můžu vás to naučit :-).

Selects are IN

Today it will be short. I will write about one small Spring feature that I have discovered recently. It is another nice functionality of SimpleJdbcTemplate.

Imagine that you want to find all account with given account numbers. Since we want to gain maximal performance we want to do it in one SQL statement.

SELECT ACCOUNT_NUMBER, BALANCE FROM ACCOUNT WHERE ACCOUNT_NUMBER IN (?, ?)

Using Spring it is easy. The only trouble is, that I do not know number of account numbers beforehand. Therefore I do not know the number of question marks needed in the SQL query. Of course I can create the statement dynamically using string manipulation, but the code would be messy. Fortunately Spring comes to rescue. If I use named parameters in SimpleJdbcTemplate, Spring will automatically do everything for me. So the code will be nice and simple as it should be

	public List<Account> findAccounts(Set<String> accountNumbers)
	{
		return getSimpleJdbcTemplate().query(
				"SELECT ACCOUNT_NUMBER, BALANCE FROM ACCOUNT WHERE ACCOUNT_NUMBER IN (:accountNumbers)",
				ACCOUNT_ROW_MAPPER,
				Collections.singletonMap("accountNumbers", accountNumbers)
		);
	}

Nice, isn’t it? Source code can be found in SVN. More details are in the Spring documentation.

Simple JDBC template

Občas se mi stane, že nepotřebuji celou tu mašinérii kolem ORM. Chci jenom přistupovat do databáze. Zavolat pár insertů a pár selectů. V té chvíli přichází na řadu JDBC. Je samozřejmě možné používat JDBC přímo, ale jak říká Rod Johnson, je to „sackable offence“. (česky asi důvod k vyhazovu). Zvládnout bez chyby všechny ty try, catche, finally, zavírání connection, result setů a kdoví čeho ještě je jen pro zkušené a velmi disciplinované vývojáře. Spring odjakživa poskytoval rozhranní JdbcTemplate, které krásně usnadňovalo volání JDBC a staralo se o všechnu tu nudnou a nebezpečnou práci se správou zdrojů. Chybělo mi tam ale několik věcí. První z nich bylo pojmenování parametrů jak je známe třeba z HQL. Prostě jsem místo všech těch otazníků v query chtěl psát jména parametrů. Chybělo mi také lepší provázanost s Javou 5 a generiky. (To mi mimochodem dost chybí i u JPA query, která je novější než Java 5).

Nevím jestli se vám to také děje. Já když už umím nějakou technologii, tak se mi občas stane, že mi její zajímavé novinky občas uniknou. Podobně mi uniklo zavedení SimpleJdbcTemplate do Springu 2.0. SimpleJdbcTemplate totiž všechny mé stesky řeší. Nejlépe si to ukážeme na příkladě (ten je převzatý z mého skvělého a nepostradatelného projektu MvnIndex.org, který si teď všichni povinně stáhnete a začnete ho používat, nebo se naštvu a už nikdy nic podobného nenapíšu).

Nejlepší je naimplementovat DAO, které rozšiřuje SimpleJdbcDaoSupport. Tím si ušetříme spoustu práce, stačí nám už jen injektnout do této třídy DataSource. Pak už máme k dispozici SimpleJdbcTemplate a můžeme si dovolit například takovéto volání:



  public List<BasicArtifactInfo> searchArtifacts(String searchExpression, PagingInfo pagingInfo) {
    Map<String,Object> args = new HashMap<String, Object>();
    args.put("searchExpression""%"+searchExpression+"%");
    args.put("from", pagingInfo.getFrom());
    args.put("size", pagingInfo.getSize());
    return getSimpleJdbcTemplate().query(
          "select distinct group_id, artifact_id from ARTIFACT where artifact_id like :searchExpression or group_id like :searchExpression order by if(group_id like :searchExpression, 0, 1), group_id, artifact_id limit :from, :size"
          BASIC_ARTIFACT_INFO_MAPPER , 
          args);
  }

Vidíme že atributy dotazu dáme jednoduše do mapy, zavoláme dotaz, poskytneme mapovač výsledků a je to. V dotazu můžeme použít jména parametrů stejně jako to známe z HQL. Takže můžeme například psát „... where artifact_id like :searchExpression or group_id like :searchExpression...“. Pro mapování výsledků musíme implementovat jednoduché rozhraní ParameterizedRowMapper.



  private static final ParameterizedRowMapper<BasicArtifactInfo> BASIC_ARTIFACT_INFO_MAPPER = new ParameterizedRowMapper<BasicArtifactInfo>(){
    public BasicArtifactInfo mapRow(ResultSet rs, int rowNumthrows SQLException {
      return new BasicArtifactInfo(rs.getString("group_id"), rs.getString("artifact_id"));
    }
  };

Vidíme, že generika jsou tu plně podporována, takže si nemusíme dělat hlavu s přetypováváním, varováními překladače atp. Super ne? A to jsem zapomněl na to hlavní. Spring se mi postará o vytvoření PreparedStatementů, správu transakcí, mapování vyjímek a prostě o všechno na co jsme od něj zvyklí.

Takže pokud je pro vás Hibernate moc těžkopádný a nechcete se učit iBatis, pokud chcete jen volat JDBC, zkuste se podívat na SimpleJdbcTemplate, stojí to za to.

Celou třídu si můžete prohlédnout v SVN.