Spring Batch 3. díl

V minulém díle jsme si vytvořili job, který obsahoval několik kroků (Step). Každému kroku byl přiřazen jeden tasklet, který implementoval celou funkcionalitu. Dost často ale krok v jobu bude načítat data, zpracovávat je a nakonec zapisovat. Pro tento případ je tady chunk. V rámci chunku je možné definovat reader, processor a writer.

<chunk reader="myReader" writer="myWriter" processor="myProcessor">

V následujícím příkladu si ukážeme job, který se bude skládat z jediného kroku, během kterého zavoláme náš reader, poté data vrácená z readeru zpracujeme ve vlastním processoru a nakonec data zapíšem writerem.

context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/batch"
	xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-3.0.xsd">
	
	<beans:bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
		<beans:property name="transactionManager" ref="transactionManager" />
	</beans:bean>
	
	<beans:bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
		<beans:property name="jobRepository" ref="jobRepository" />
	</beans:bean>
	
	<beans:bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
</beans:beans>

job.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/batch"
	xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
						http://www.springframework.org/schema/batch
						http://www.springframework.org/schema/batch/spring-batch-3.0.xsd">
	<beans:import resource="context.xml" />
	
	<beans:bean id="myReader" class="cz.vitfo.MyReader"/>
	<beans:bean id="myProcessor" class="cz.vitfo.MyProcessor"/>
	<beans:bean id="myWriter" class="cz.vitfo.MyWriter"/>
	
	<job id="myJob">
		<step id="myStep">
			<tasklet>
				<chunk reader="myReader" writer="myWriter" processor="myProcessor" commit-interval="4"/>
			</tasklet>
		</step>
	</job>
</beans:beans>

Třída MyInput představuje objekty, které budou načítány (vraceny) readerem.

Číst dálSpring Batch 3. díl

Rozsah singleton a prototype ve Springu

Spring vytváří beany (instance) a je zodpovědný za jejich životní cyklus. To ale platí pouze v případě, že bean je ve scope singleton. V zásadě můžeme rozdělit beany podle scope na dvě skupiny: singleton a prototype. Singleton znamená, že daná beana (instance) se v kontextu (v aplikaci) vyskytuje pouze v jedné jediné instanci. Pokaždé, když zavoláme metodu getBean(), dostaneme referenci na tu jedinou existující instanci. Pokud je ale scope beany prototype, při každém volání getBean() dostaneme novou instanci.
Nastavení scope se prování při definici dané beany.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean class="cz.vitfo.spring.MyClass01" 
   		init-method="myInit" 
   		destroy-method="myDestroy"
   		scope="singleton">
   </bean>
   <bean class="cz.vitfo.spring.MyClass02" 
   		init-method="myInit" 
   		destroy-method="myDestroy"
   		scope="prototype">
   </bean>
</beans>

Každé beaně jsem nastavil init a destroy metodu (tomuto tématu jsem se věnoval v tomto příspěvku) a scope (rozsah).

Číst dálRozsah singleton a prototype ve Springu

Formátování JSON souboru v programu Notepad++

Pokud pracujete se soubory s JSON, je možné je v programu Notepad++ nechat naformátovat do lidsky čitelnější podoby. K tomu je potřeba si do Notepad++ nainstalovat plugin JSTool: Pluginy -> Plugin Manager -> Show Plugin Manager -> z dostupných pluginů vybrat JSTool. Následně je možné soubor s json naformátovat pomocí Pluginy -> JSTool -> JSFormat, nebo použít klávesovou zkratku Ctrl + Alt + M.

Před

notepad_formating_json_01

Po

notepad_formating_json_02

Fatal error compiling: invalid target release: 1.8

Tuto chybu můžete dostat v případě, že jste používali starší verzi (například Javu 7) a nyní chcete používat verzi novější (v tomto případě Javu 8). Je třeba nastavit nejen

  • SDK pro projekt na 1.8: otevřít Project Settings (Alt + Shift + Ctrl + S) -> nastavit SDK na 1.8
  • Java compiler na verzi 1.8: File -> Settings -> Build, Execution, Deployment -> Compiler -> Java Compiler

ale také nastavit Maven runner na Javu 1.8 (pokud takto není nastaven způsobuje tuto chybu): File -> Settings -> Build, Execution, Deployment -> Build Tools -> Maven -> Runner -> nastavit JRE na 1.8.

Spring Batch 2. díl

V předchozím díle jsme se věnovali představení frameworku String Batch, vysvětlení základních pojmů a ukázali jsme si jednoduchý projekt. V tomto díle vytvoříme další projekt.

Job je posloupnost jednotlivých kroků (Step). Krok pak obsahuje úkol (Tasklet), který popisuje (implementuje) co se má dělat. Task v sobě ještě může obsahovat další kousek (Chunk), ale tomu se budemě věnovat někdy příště.

Vaříme čaj se Spring Batch.
V následujícím příkladu si ukážeme job, který se bude skládat s pěti kroků (Tasklet). Budeme připravovat čaj.

  1. naplníme konvici vodou
  2. zapneme konvici
  3. dáme do konvice čaj
  4. počkáme tři minuty
  5. hotový čaj naservírujeme

Struktura projektu

│   pom.xml
│
└───src
    ├───main
    │   └───java
    │       └───cz
    │           └───vitfo
    │               │   ExecuteJob.java
    │               │
    │               └───tasklets
    │                       FillKettleWithWater.java
    │                       PutTeaToWater.java
    │                       ServeTea.java
    │                       TurnOnKettle.java
    │                       WaitThreeMinutes.java
    │
    └───resources
            context.xml
            job.xml


Všimněte si, že ve složce resources máme dva xml. Spring umožňuje rozdělit konfiguraci do více souborů. Jednou z možností je potom pro vytvoření kontextu předat list konfiguračních souboru (ukázáno v tomto příspěvku) a nebo importovat jeden konfigurační soubor do druhého pomocí elementu <import> jak je ukázáno v tomto příkladu (řádek osm v souboru job.xml).

Číst dálSpring Batch 2. díl

Další anotace v JAXB

V jednom z předchozích příspěvků jsem se věnoval základní práci s JAXB, kde jsem používal pouze dvě anotace a to @XmlRootElement a @XmlType. Podívejme se na část vytvořeného xml souboru.

<company>
    <centers>
        <code>METPRG</code>
        <description>Office center Metronom in Prague</description>
        <rooms>
            <code>A02</code>
            <numberOfSeats>14</numberOfSeats>
        </rooms>
        <rooms>
            <code>A13</code>
            <numberOfSeats>42</numberOfSeats>
        </rooms>
        <rooms>
            <code>B08</code>
            <numberOfSeats>22</numberOfSeats>
        </rooms>
    </centers>
    <centers>
        <code>AVIPRG</code>
        <description>Office center Aviatica in Prague</description>
        <rooms>
            <code>AA41</code>
            <numberOfSeats>70</numberOfSeats>
        </rooms>
        <rooms>
            <code>CA12</code>
            <numberOfSeats>38</numberOfSeats>
        </rooms>
        ...

Company obsahuje seznam elementů center a ty v sobě mají seznam elementů room. Když se podíváte pečlivěji, uvidíte, že každý element center se jmenuje centers a každý element room se jmenuje rooms. Určitě by to lépe vypadalo, kdyby se tyto elementy jmenovaly center a room. Nebo možná ještě lépe, kdyby se element center jmenoval jednoznačněji business_center. Jak center tak room obsahují element code. Bylo čitelnější, kdyby se jmenovaly center_code a room_code. A také kořenový element company by mohl obsahovat ještě atribut s názvem společnosti. To vše je možné, když použijeme další anotace, které JAXB nabízí.

Anotace @XmlAccessorType říká, že každý field (nestatická a netransientní proměnná – to jsou v tomto příkladu všechny) bude svázán s xml elementem.
Anotaci @XmlElementWrapper použijeme, když pracujeme se seznamem. Vytvoříme element, který bude v sobě obsahovat jednotlivé elementy seznamu.
Anotace @XmlAttribute nevytvoří xml element, ale atribut. Pomocí hodnoty, kterou přiřadíme do name nastavíme atributu název.
Když použijeme anotaci @XmlElement, můžeme předat hodnotu name a tím nastavíme názvem xml elementu.

Jasnější to bude na příkladu.

Číst dálDalší anotace v JAXB

Ukládání Java 8 LocalDateTime do JSON pomocí Jackson

V případě, že se pokusíte uložit Java 8 LocalDateTime do formátu JSON pomocí knihovny Jackson, dostanete následující výsledek:

"from": {
	"hour": 14,
	"minute": 30,
	"nano": 0,
	"second": 0,
	"monthValue": 8,
	"year": 2016,
	"dayOfMonth": 21,
	"dayOfWeek": "SUNDAY",
	"dayOfYear": 234,
	"month": "AUGUST",
	"chronology": {
		"id": "ISO",
		"calendarType": "iso8601"
	}
}

Ve většině případů to asi nebude přesně to, co byste chtěli. Jackson nabízí jednoduché řešení a to registraci JavaTimeModule. Tento modul umožní správné uložení a načtení Java 8 DateTime typů.

Číst dálUkládání Java 8 LocalDateTime do JSON pomocí Jackson

Java objekty do a z JSON pomocí Jackson

JSON (JavaScript Object Notation) je jednoduchý formát pro výměnu dat. Je zabalen do složených závorek a obsahuje dvojice klíč : hodnota. Na rozdíl od xml není tak upovídaný.

Jackson je populární javovská knihovna pro převod Java objektů do JSON a naopak.

Nejjednodušším způsobem jak převést Java objekt na JSON formát a naopak je použít com.fasterxml.jackson.databind.ObjectMapper. Pro to, abychom mohli ObjectMapper použít je třeba do pom.xml (v Maven projektu) přidat závislost na následující knihovně. Nejaktuálnější verze v době psaní tohoto příspěvku je 2.8.5:

<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
	<version>2.8.5</version>
</dependency>

Vytvoříme si objekt, který budeme ukládat a načítat do JSON formátu.

MyObject.java

public class MyObject {

	private long myNumber;
	private boolean myBoolean;
	private String myString;
	
	public MyObject(long myNumber, boolean myBoolean, String myString) {
		this.myNumber = myNumber;
		this.myBoolean = myBoolean;
		this.myString = myString;
	}
	
	// Pro načítání z JSON je třeba mít bezparametrický konstruktor.
	public MyObject() {}

	public long getMyNumber() {
		return myNumber;
	}

	public void setMyNumber(long myNumber) {
		this.myNumber = myNumber;
	}

	public boolean isMyBoolean() {
		return myBoolean;
	}

	public void setMyBoolean(boolean myBoolean) {
		this.myBoolean = myBoolean;
	}

	public String getMyString() {
		return myString;
	}

	public void setMyString(String myString) {
		this.myString = myString;
	}
}

V hlavní třídě vytvoříme instanci object mapperu, instanci našeho objektu a pro ukládání voláme metodu writeValue(), pro načtení readValue().

Číst dálJava objekty do a z JSON pomocí Jackson