<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	>

<channel>
	<title>Jiří Hradil blog</title>
	<atom:link href="http://www.hradil.org/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.hradil.org</link>
	<description>o software</description>
	<pubDate>Wed, 25 Aug 2010 20:49:39 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Rails and the Enterprise</title>
		<link>http://www.hradil.org/rails-and-the-enterprise/</link>
		<comments>http://www.hradil.org/rails-and-the-enterprise/#comments</comments>
		<pubDate>Wed, 25 Aug 2010 20:42:09 +0000</pubDate>
		<dc:creator>Jiří Hradil</dc:creator>
		
		<category><![CDATA[Ruby on Rails]]></category>

		<guid isPermaLink="false">http://www.hradil.org/?p=339</guid>
		<description><![CDATA[Posílám skvělý článek Rails and the Enterprise, který je povinností pro každého programátora v Ruby on Rails.
Zajímá mě:

jak byste s ohledem na obsah článku definovali &#8220;enterprise&#8221;?
kde je hranice, od které považujete systém za &#8220;velký&#8221;?
proč a jak jsou některé jazyky či frameworky vhodnější pro vývoj &#8220;velkých&#8221; systémů?
kdo či co je autorita, která definuje tuto &#8220;vhodnost&#8221;?

]]></description>
			<content:encoded><![CDATA[<p>Posílám skvělý článek <a href="http://weblog.rubyonrails.org/2010/3/24/rails-and-the-enterprise?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed:+RidingRails+(Riding+Rails)" onclick="javascript:pageTracker._trackPageview('/outbound/article/weblog.rubyonrails.org');">Rails and the Enterprise</a>, který je povinností pro každého programátora v Ruby on Rails.</p>
<p>Zajímá mě:</p>
<ol>
<li>jak byste s ohledem na obsah článku definovali &#8220;enterprise&#8221;?</li>
<li>kde je hranice, od které považujete systém za &#8220;velký&#8221;?</li>
<li>proč a jak jsou některé jazyky či frameworky vhodnější pro vývoj &#8220;velkých&#8221; systémů?</li>
<li>kdo či co je autorita, která definuje tuto &#8220;vhodnost&#8221;?</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.hradil.org/rails-and-the-enterprise/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Active Record a transakce</title>
		<link>http://www.hradil.org/active-record-a-transakce/</link>
		<comments>http://www.hradil.org/active-record-a-transakce/#comments</comments>
		<pubDate>Tue, 24 Aug 2010 08:26:59 +0000</pubDate>
		<dc:creator>Jiří Hradil</dc:creator>
		
		<category><![CDATA[Ruby on Rails]]></category>

		<guid isPermaLink="false">http://www.hradil.org/?p=306</guid>
		<description><![CDATA[Použití transakcí je v Active Record opravdu triviální. Samotná dokumentace k modulu ActiveRecord::Transactions::ClassMethods je jednoduchá a ovládnutelná za 5 minut. Zapomeňme na dlouhá studia románů typu Spring transaction management, zapomeňte na AOP, advisory a jiné ptákoviny. Nepoužíváme technologii pro technologii. Transakce potřebujeme jednoduše proto, aby data v databázi byla v každém okamžiku konzistentní. Pro polévku [...]]]></description>
			<content:encoded><![CDATA[<p>Použití transakcí je v Active Record opravdu triviální. Samotná dokumentace k modulu <a href="http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/api.rubyonrails.org');">ActiveRecord::Transactions::ClassMethods</a> je jednoduchá a ovládnutelná za 5 minut. Zapomeňme na dlouhá studia románů typu <a href="http://static.springsource.org/spring/docs/2.0.8/reference/transaction.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/static.springsource.org');">Spring transaction management</a>, zapomeňte na AOP, advisory a jiné ptákoviny. Nepoužíváme technologii pro technologii. Transakce potřebujeme jednoduše proto, aby data v databázi byla v každém okamžiku konzistentní. Pro polévku kolem odkazuji třeba na <a href="http://cs.wikipedia.org/wiki/Datab%C3%A1zov%C3%A1_transakce" onclick="javascript:pageTracker._trackPageview('/outbound/article/cs.wikipedia.org');">Wikipedii</a>.</p>
<h2>Automatické transakce pro create, save a destroy</h2>
<p>Pokud voláme jen jednu metodu <em>create</em>, <em>save</em> nebo <em>destroy</em>, transakce nemusíme řešit, Active Record volání metod obalí transakcí automaticky:<br />
<code><br />
Contact.create(:name=&gt;'Jirka Hradil')<br />
</code><br />
Vygeneruje:<br />
<code><br />
BEGIN<br />
INSERT INTO "contacts" ("name") VALUES('Jirka Hradil') RETURNING "id"<br />
COMMIT<br />
</code></p>
<p>Je to logické - 1 volání <em>create</em>, <em>save</em> nebo <em>destroy</em> buď je nebo není provedeno. Toto volání už nemáme jak kouskovat. Samozřejmě pokud je insert, update nebo delete pouze 1, nebylo by třeba vůbec transakci používat, samostatný sql statement je atomický sám o sobě.</p>
<p>Stejně můžeme uložit více objektů najednou, pokud se ovšem vejdeme do volání 1 metody:<br />
<code><br />
Contact.create(:name=&gt;'Jirka Hradil', :addresses=&gt;[Address.new(:address=&gt;'Pod Valhallou 1')])<br />
</code><br />
Vygeneruje:<br />
<code><br />
BEGIN<br />
INSERT INTO "contacts" ("name") VALUES('Jirka Hradil') RETURNING "id"<br />
INSERT INTO "addresses" ("address", "contact_id") VALUES('Pod Valhallou 1', 4) RETURNING "id"<br />
COMMIT<br />
</code></p>
<h2>Ruční transakce přes více metod</h2>
<p>Pokud potřebujeme zavolat více metod, které musí být provedeny všechny najednou nebo žádná z nich, obalíme je metodou <em>transaction</em>, která je jak metodou třídy, tak instance každého modelu (třídy jsou v Ruby také objekty):<br />
<code><br />
c = Contact.new(:name=>'Jirka Hradil')<br />
a = Address.new(:address=>'Pod Valhallou 1', :contact=>c)</p>
<p>Contact.transaction do #tady by klidně mohlo být c.transaction nebo Address.transaction<br />
c.save!<br />
a.save!<br />
end<br />
</code><br />
Vygeneruje stejné:<br />
<code><br />
BEGIN<br />
INSERT INTO "contacts" ("name") VALUES('Jirka Hradil') RETURNING "id"<br />
INSERT INTO "addresses" ("address", "contact_id") VALUES('Pod Valhallou 1', 5) RETURNING "id"<br />
COMMIT<br />
</code></p>
<p>Pokud používáme jen jedno databázové spojení (default nastavení Rails), je jedno, ze kterého modelu metodu <em>transaction</em> použijeme, tuto metodu každý model dědí z <a href="http://api.rubyonrails.org/classes/ActiveRecord/Base.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/api.rubyonrails.org');">ActiveRecord::Base</a>, který includuje modul <a href="http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/api.rubyonrails.org');">ActiveRecord::Transactions::ClassMethods</a>.</p>
<h2>Transakce napříč databázovými spojeními</h2>
<p>Tuto možnost jsem nikdy nezkoušel, ale dle dokumentace stačí vnořit volání metod transaction do sebe napříč modely, které jsou ukládány do různých databází:<br />
<code><br />
c = Contact.new(:name=>'Jirka Hradil')<br />
a = Address.new(:address=>'Pod Valhallou 1', :contact=>c)</p>
<p>Contact.transaction do<br />
Address.transaction do<br />
c.save!<br />
a.save!<br />
end<br />
end<br />
</code><br />
Plně distribuované transakce napříč různými databázemi Active Record nepodporuje. Já osobně jsem tohle nikdy nepotřeboval a dávám si moc záležet, abych o distribuované transakce ani nezavadil, ale pro někoho jejich absence může být omezením.</p>
<h2>A co rollback a commit?</h2>
<p>Commit neřešíme. Pokud žádné volání metod uvnitř transakčního bloku nevyhodí výjimku, na konci bloku se provede commit automaticky.</p>
<p>Rollback také nemusíme řešit. Vyvolá se sám, pokud některá z metod vyhodí výjimku.</p>
<h2>Na co si dávat pozor při použití save namísto save!</h2>
<p>Pokud voláme více metod, které obalíme do transakčního bloku a pro ukládání změn voláme <em>save</em> namísto <em>save!</em>, pak pozor - metoda <em>save</em> (bez vykřičníku) nevyhazuje výjimku a tedy se ani neprovede rollback v případě neuložení objektu (třeba když neprojde validace). Výsledkem je nekonzistentní stav, který je na konci potvrzen &#8220;sprostým&#8221; commitem:<br />
<code><br />
Contact.transaction do<br />
c.save #nevyhodí výjimku, ale nemusí se uložit do db, pokud např. neprojde validace<br />
a.save #nevyhodí výjimku, ale nemusí se uložit do db, pokud např. neprojde validace<br />
end<br />
</code><br />
Pokud třeba selhala validace u adresy, vygeneruje se jen insert pro kontakt:<br />
<code><br />
BEGIN<br />
INSERT INTO "contacts" ("name") VALUES('Jirka Hradil') RETURNING "id"<br />
COMMIT<br />
</code><br />
&#8230;a databázi máme v nekonzistentním stavu. Což je logické, protože jak jsem uvedl, rollback se zavolá jen tehdy, pokud něco v transakčním bloku vyhodí výjimku.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hradil.org/active-record-a-transakce/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Active Record a propojení objektů</title>
		<link>http://www.hradil.org/active-record-a-propojeni-objektu/</link>
		<comments>http://www.hradil.org/active-record-a-propojeni-objektu/#comments</comments>
		<pubDate>Sun, 15 Aug 2010 11:54:33 +0000</pubDate>
		<dc:creator>Jiří Hradil</dc:creator>
		
		<category><![CDATA[Ruby on Rails]]></category>

		<guid isPermaLink="false">http://www.hradil.org/?p=270</guid>
		<description><![CDATA[Propojení objektů je v Active Record velmi jednoduché a pokud znáte ORM, zabere vám pochopení několik minut. Nemá smysl přepisovat napsané a zájemce odkazuji na ActiveRecord::Associations::ClassMethods. Active Record má pro asociace opravdu hodně možností, ale pro základní použití vám bude stačit jen minimum z nich.
Pro jednoduchost si projdeme vztahy one-to-one a one-to-many.
One to one
&#8220;Kontakt má [...]]]></description>
			<content:encoded><![CDATA[<p>Propojení objektů je v <a href="http://ar.rubyonrails.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/ar.rubyonrails.org');">Active Record</a> velmi jednoduché a pokud znáte ORM, zabere vám pochopení několik minut. Nemá smysl přepisovat napsané a zájemce odkazuji na <a href="http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/api.rubyonrails.org');">ActiveRecord::Associations::ClassMethods</a>. Active Record má pro asociace opravdu hodně možností, ale pro základní použití vám bude stačit jen minimum z nich.</p>
<p>Pro jednoduchost si projdeme vztahy one-to-one a one-to-many.</p>
<h2>One to one</h2>
<p><em>&#8220;Kontakt má jednu adresu&#8221;</em> definujeme pomocí metod modulu <a href="http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/api.rubyonrails.org');">ActiveRecord::Associations::ClassMethods</a> a to <em>has_one</em> a <em>belongs_to</em>. Netřeba se obávat, nic nemusíme importovat, includovat, requirovat ani nic podobného. Vše potřebné již udělal Active Record. Rozdíl mezi <em>has_one</em> a <em>belongs_to</em> metodami je v tom, že strana, která má <em>belongs_to</em> obsahuje cizí klíč v db tabulce. Teoreticky nemusíme vůbec <em>has_one</em> definovat, ale potom se připravíme o možnost jednoduše objektově dosáhnout na adresu z kontaktu.</p>
<p><code><br />
class Contact &lt; ActiveRecord::Base<br />
has_one :address<br />
end<br />
</code></p>
<p><code><br />
class Address &lt; ActiveRecord::Base<br />
belongs_to :contact<br />
end<br />
</code></p>
<p>Databázové tabulky:</p>
<p><code><br />
CREATE TABLE contacts (<br />
id integer NOT NULL PRIMARY KEY,<br />
name varchar(255)<br />
);<br />
</code></p>
<p><code><br />
CREATE TABLE addresses (<br />
id integer NOT NULL PRIMARY KEY,<br />
contact_id integer NOT NULL,<br />
address varchar(255)<br />
);<br />
</code></p>
<p>Ukládáme:<br />
<code><br />
c = Contact.new(:name=>'Jirka Hradil', :address=>Address.new(:address=>'Valhalla'))<br />
c.save<br />
</code><br />
Tento kód nám vygeneruje potřebné inserty:<br />
<code><br />
BEGIN<br />
INSERT INTO "contacts" ("name") VALUES('Jirka Hradil') RETURNING "id"<br />
INSERT INTO "addresses" ("address", "contact_id") VALUES('Valhalla', 1) RETURNING "id"<br />
COMMIT<br />
</code></p>
<p>Všimněte si automatické transakce okolo obou insertů. Vzpomínáte si, že bychom je někde definovali? Ne, Active Record to za nás udělal automaticky. Na kontaktu jsme zavolali metodu <em>save</em>, což je 1 &#8220;atomické&#8221; volání metody, tudíž se vše obalilo do 1 transakce.<br />
Samozřejmě bychom mohli nejdřív uložit kontakt a teprve pak adresu, pak bychom si ovšem hranice transakce museli řídit sami. To si ukážeme příště.</p>
<p>A teď vyhledáváme:<br />
<code><br />
c = Contact.first #najdeme první kontakt<br />
puts c.address #a vypíšeme jeho adresu<br />
</code></p>
<p>Což nám generuje selecty:<br />
<code><br />
SELECT * FROM "contacts" LIMIT 1<br />
SELECT * FROM "addresses" WHERE ("addresses".contact_id = 1) LIMIT 1<br />
</code></p>
<p>Tady si všimněte, že nikde nedefinuji eager, lazy ani nic podobného. Active Record standardně používá lazy inicializaci a prostě pokud mu chybí adresa, tak si ji při prvním použití dotáhne přes samostatný select. Podle mě v Active Record 2.3.x neexistuje default způsob, jak zajistit, aby se při výběru kontaktu VŽDY joinovala automaticky jedna jeho adresa (lze však použít <em>Contact.find_by_sql</em> a select si napsat dle libosti). Eager inicializace se používají při vztahu <em>has_many</em> a <em>has_and_belongs_to_many</em> a fungují na principu dotažení všech podřízených entit najednou v samostatném subselectu, jak si ukážeme.</p>
<h2>One to many</h2>
<p><em>&#8220;Kontakt má hodně adres&#8221;</em> definujeme pomocí <em>has_many</em> a adresy přepíšeme do množného čísla na <em>addresses</em>.<br />
<code><br />
class Contact < ActiveRecord::Base<br />
    has_many :addresses<br />
end<br />
</code></p>
<p>Třída Address zůstává stejná (má jen belongs_to), stejně tak databázové schéma.</p>
<p>Teď uložíme kontakt s více adresami najednou. Všimněte si použití slova <em>addresses</em> jako množného čísla. Adresy jsou rovněž pole, nikoli samostatný objekt:<br />
<code><br />
c = Contact.new(:name=>'Jirka Hradil', :addresses=>[Address.new(:address=>'První adresa'), Address.new(:address=>'Druhá adresa')])<br />
c.save<br />
</code><br />
Vygenerované inserty:<br />
<code><br />
BEGIN<br />
INSERT INTO "contacts" ("name") VALUES('Jirka Hradil') RETURNING "id"<br />
INSERT INTO "addresses" ("address", "contact_id") VALUES('První adresa', 2) RETURNING "id"<br />
INSERT INTO "addresses" ("address", "contact_id") VALUES('Druhá adresa', 2) RETURNING "id<br />
COMMIT<br />
</code><br />
Opět, vše automaticky zabaleno do transakce, protože jsme volali jen 1 &#8220;atomické&#8221; save.<br />
</code><br />
Vyhledáváme:<br />
<code><br />
c = Contact.first #najdeme první kontakt<br />
puts c.addresses #pole adres<br />
</code></p>
<p>Vygenerované selecty:<br />
<code><br />
SELECT * FROM "contacts" LIMIT 1<br />
SELECT * FROM "addresses" WHERE ("addresses".contact_id = 1)<br />
</code></p>
<h3>Lazy vs eager</h3>
<p>Active Record je při načítání podřízených objektů velmi chytrý. Zapomeňte na nějakou LazyInitializationException nebo podobné ptákoviny, které vás jen stojí spoustu času. Podřízené objekty se natahují vždy přes samostatný select.<br />
<code><br />
Contact.all.each {|c| puts c.addresses} # vypíšeme si všechny adresy<br />
</code><br />
Generované selecty:<br />
<code><br />
SELECT * FROM "contacts"<br />
SELECT * FROM "addresses" WHERE ("addresses".contact_id = 1)<br />
SELECT * FROM "addresses" WHERE ("addresses".contact_id = 2)<br />
</code><br />
&#8230;což nám vede ke známému &#8220;N+1&#8243; select problému - máme 2 adresy a udělají se celkem 3 dotazy.<br />
Eliminace je jednoduchá pomocí atributu <em>:include</em><br />
<code><br />
Contact.all(:include=>:addresses).each {|c| puts c.addresses}<br />
</code><br />
Pak se selecty transformují do 1+1, kdy je jeden select na všechny kontakty a další select na všechny adresy kontaktů, načtených v předchozím selectu:<br />
<code><br />
SELECT * FROM "contacts"<br />
SELECT "addresses".* FROM "addresses" WHERE ("addresses".contact_id IN (1,2))<br />
</code><br />
Jednoduché a efektivní.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hradil.org/active-record-a-propojeni-objektu/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Active Record a automatické findery</title>
		<link>http://www.hradil.org/active-record-a-automaticke-findery/</link>
		<comments>http://www.hradil.org/active-record-a-automaticke-findery/#comments</comments>
		<pubDate>Sun, 08 Aug 2010 15:22:43 +0000</pubDate>
		<dc:creator>Jiří Hradil</dc:creator>
		
		<category><![CDATA[Ruby on Rails]]></category>

		<guid isPermaLink="false">http://www.hradil.org/?p=240</guid>
		<description><![CDATA[Jako další pěknou vlastnost Active Record vypíchnu automatické findery.
Zděděním modelu (Ruby třídy, která reprezentuje &#8220;business object&#8221;) od ActiveRecord::Base získáváme automatické findery na všechny atributy, načtené reflexí z databázové tabulky i včetně jejich kombinací.
Příklad:
Stejně jako v minulém příspěvku použijeme třídu Contact:

class Contact &#60; ActiveRecord::Base
#to je vsechno
end

A její tabulku contacts:

CREATE TABLE contacts (
id integer NOT NULL PRIMARY KEY,
name [...]]]></description>
			<content:encoded><![CDATA[<p>Jako další pěknou vlastnost <a href="http://ar.rubyonrails.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/ar.rubyonrails.org');">Active Record</a> vypíchnu automatické findery.</p>
<p>Zděděním modelu (Ruby třídy, která reprezentuje &#8220;business object&#8221;) od <em>ActiveRecord::Base</em><span> získáváme automatické findery na všechny atributy, načtené reflexí z databázové tabulky i včetně jejich kombinací.</span></p>
<p>Příklad:</p>
<p>Stejně jako v <a href="http://www.hradil.org/active-record-je-nejlepsi-orm/" onclick="">minulém příspěvku</a> použijeme třídu Contact:<br />
<code><br />
class Contact &lt; ActiveRecord::Base<br />
#to je vsechno<br />
end<br />
</code><br />
A její tabulku <em>contacts</em>:<br />
<code><br />
CREATE TABLE contacts (<br />
id integer NOT NULL PRIMARY KEY,<br />
name varchar(255),<br />
address varchar(255)<br />
);<br />
</code><br />
Atribut <em>id</em> je default primární klíč (ve třídě jsme tohle nikde neurčili, je to default chování Active Record). Pak můžeme kontakt najít jednoduše dle id:<br />
<code><br />
c = Contact.find(1) #najde kontakt s id 1<br />
</code><br />
Metodu <em>find</em> třídy <em>Contact</em> jsme nikde nenapsali, je zděděna z <em>ActiveRecord::Base</em>. Zajímavé jsou však další &#8220;automatické findery&#8221; dle atributů tabulky, aniž bychom je kdekoli definovali:<br />
<code><br />
c = Contact.find_by_name('Jirka') #metodu find_by_name jsem nikde nepsal<br />
c = Contact.find_by_address('Valhalla') #tuhle jsem taky nikde nepsal<br />
</code><br />
Fungují dokonce kombinace atributů. Je jedno v jakém pořadí, všimněte si spojky &#8220;and&#8221; mezi atributy:<br />
<code><br />
c = Contact.find_by_name_and_address('Jirka', 'Valhalla') #nejdriv jmeno, pak adresa<br />
c = Contact.find_by_address_and_name('Valhalla', 'Jirka') #naopak<br />
</code></p>
<p>Tohle je vývojářův sen. Žádné rozhraní, žádné implementace, při změně atributů v tabulce nemusíme findery měnit či připisovat nové. Změníme pouze schéma tabulky a nové findery a jejich kombinace máme okamžitě k dispozici. <strong>Samozřejmě bez redeploy aplikace či restartu serveru</strong>.</p>
<p>Vypadá to jako magie, ale Active Record využívá Ruby metodu <a href="http://ruby-doc.org/core/classes/Kernel.html#M005925" onclick="javascript:pageTracker._trackPageview('/outbound/article/ruby-doc.org');">method_missing</a>, která se zavolá vždy, když metoda v objektu neexistuje.  V tomto případě si <em>method_missing</em> sáhne do schématu tabulku a za běhu přidá novou metodu dle kombinace atributů.</p>
<p>Protože máme logiku i data pěkně pohromadě, pak se nemusíme rozpomínat či studovat, jaké rozhraní používat pro hledání, jaké pro ukládání, co pro změnu či mazání, apod. Prostě potřebuju pracovat s kontaktem, tak použiju třídu <em>Contact </em>či některou z jejich instancí.</p>
<p>Příklad:<br />
<code><br />
c1 = Contact.new(:name=>'Hradil')<br />
c1.save #ulozime novy kontakt<br />
c1.name = 'Jirka Hradil' #zmenime jmeno<br />
c1.save #ulozime zmeny, provede se UPDATE, protoze zaznam uz byl persistovan a ma id<br />
c2 = Contact.find_by_name('Jirka Hradil') #najdeme si kontakt<br />
c2.delete #smazeme kontakt, provede se DELETE<br />
</code></p>
<p>Zkuste si to a uvidíte, jak neradi se budete vracet k Hibernate :).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hradil.org/active-record-a-automaticke-findery/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Active Record je nejlepší ORM</title>
		<link>http://www.hradil.org/active-record-je-nejlepsi-orm/</link>
		<comments>http://www.hradil.org/active-record-je-nejlepsi-orm/#comments</comments>
		<pubDate>Tue, 03 Aug 2010 20:23:45 +0000</pubDate>
		<dc:creator>Jiří Hradil</dc:creator>
		
		<category><![CDATA[Ruby on Rails]]></category>

		<guid isPermaLink="false">http://www.hradil.org/?p=212</guid>
		<description><![CDATA[Při vývoji webových aplikací pomocí Ruby on Rails se okamžitě setkáme s potřebou ukládat objekty do databáze. Ruby on Rails používají Active Record, což je první ORM, které je opravdu radost používat. Ve srovnání třeba s Toplinkem nebo Hibernate mě nijak nebrzí a neuvěřitelně urychluje vývoj.
Za pozornost stojí:
Konvence před konfigurací (Convention over Configuration)
A to narozdíl třeba [...]]]></description>
			<content:encoded><![CDATA[<p>Při vývoji webových aplikací pomocí <a href="http://rubyonrails.org" onclick="javascript:pageTracker._trackPageview('/outbound/article/rubyonrails.org');">Ruby on Rails</a> se okamžitě setkáme s potřebou ukládat objekty do databáze. Ruby on Rails používají <a href="http://ar.rubyonrails.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/ar.rubyonrails.org');">Active Record</a>, což je první ORM, které je opravdu radost používat. Ve srovnání třeba s Toplinkem nebo Hibernate mě nijak nebrzí a neuvěřitelně urychluje vývoj.</p>
<p>Za pozornost stojí:</p>
<h2>Konvence před konfigurací (Convention over Configuration)</h2>
<p>A to narozdíl třeba od Springu bez keců. Žádné XML, žádné mapování databázových atributů do pofidérních XML objektů či anotací, nic. Stačí Ruby business objekt podědit od třídy <em>ActiveRecord::Base</em> Mapování se bere rovnou z databázové tabulky a v Ruby objektu o něm není ani zmínka.</p>
<p>Příklad:</p>
<p>Třída<br />
<code><br />
class Contact &lt; ActiveRecord::Base<br />
#to je vsechno<br />
end<br />
</code><br />
&#8230;si všechny atributy včetně datových typů načte dle databázové tabulky <em>contacts</em>:<br />
<code><br />
CREATE TABLE contacts (<br />
id integer NOT NULL,<br />
name varchar(255),<br />
address varchar(255)<br />
);<br />
</code><br />
Protože se používá CoC, nemusíme mít nikde žádný soubor, který definuje, že třída <em>Contact</em> se má podívat do <em>contacts</em>, spojení je definováno automaticky (Contact se automaticky převede na malá písmena a dá se do množného čísla).</p>
<p>Protože je použita reflexe dle struktury tabulky, ve třídě <em>Contact</em> máme opět automaticky k dispozici gettery a settery na všechny atributy z tabulky, takže můžeme klidně hned použít:<br />
<code><br />
c=Contact.new<br />
c.name='Jirka Hradil'<br />
c.address='Valhalla'<br />
</code></p>
<p>nebo ještě rychlejší:<br />
<code><br />
c = Contact.new(:name=&gt;'Jirka Hradil', :address=&gt;'Valhalla')<br />
</code></p>
<p>Tohle je, vážení kolegové, jednoduchost v ryzí formě. Napadá někoho, jak to udělat ještě líp?</p>
<h2>Data a logika pohromadě</h2>
<p>Klasický Java patternista s tímhle bude mít asi problém. Dobré je přece mít servisní vrstvu, pod ní DAO vrstvu, logika je v servisní vrstvě striktně oddělená od DAO, vše pro jistotu přes rozhraní, pak implementovat&#8230; Proč? Pokud byste někdy potřebovali změnit implementaci, tak &#8220;jen&#8221; implementujete rozhraní. Já osobně jsem si ale vždy vystačil pouze s jedinou implementací a tyto patterny mě jen zdržovaly. <em>ActiveRecord::Base</em> nám do třídy přidá rovnou instanční metody pro uložení. Takže jakmile máme vytvořen náš kontakt, ukládáme:</p>
<p><code> c.save #to je vsechno<br />
</code><br />
Tímto se vygeneruje klasický <em>INSERT INTO&#8230;</em> a záznam je persistován. Žádný service locator, žádné vytváření session, nic. Pokud potřebujeme ukládat třeba více objektů najednou nebo dát před ukládání nějakou logiku, vytvoříme si vlastní metodu a v ní si uděláme, co je třeba.</p>
<h2>YAML místo XML či properties souborů</h2>
<p><a href="http://www.yaml.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.yaml.org');">YAML</a> je formát pro serializaci či definici dat a v Rails se používá pro konfigurační soubory. Někde přece jen musí být napojení Rails aplikace do databáze definováno a tímto souborem je <em>database.yml</em>:</p>
<p><code> ...<br />
development:<br />
adapter: postgresql<br />
encoding: unicode<br />
database: book_development<br />
pool: 5<br />
username: uzivatel<br />
password: heslo<br />
...<br />
</code></p>
<p>To je v kostce veškeré napojení Active Record na konkrétní databázi a všechna potřebná konfigurace. Opět - jde to jednodušeji?</p>
<p>V některém z dalších článků se zmíním o tom, jak je to s transakcemi, asociacemi a s <em>lazy</em> vs <em>eager</em>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hradil.org/active-record-je-nejlepsi-orm/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Java vs Ruby on Rails - cesta tam a zase zpátky</title>
		<link>http://www.hradil.org/java-vs-ruby-on-rails-cesta-tam-a-zase-zpatky/</link>
		<comments>http://www.hradil.org/java-vs-ruby-on-rails-cesta-tam-a-zase-zpatky/#comments</comments>
		<pubDate>Mon, 26 Jul 2010 14:48:34 +0000</pubDate>
		<dc:creator>Jiří Hradil</dc:creator>
		
		<category><![CDATA[Ruby on Rails]]></category>

		<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://www.hradil.org/?p=196</guid>
		<description><![CDATA[Historie
Než se dostanu k jádru věci, dovolte mi malou cestu časem.
Někdy kolem roku 1999 jsem se jako první jazyk začal učit PHP. Zadáním bylo vytvoření interního web systému pro hodnocení zaměstnanců. Protože jsem se v jazycích nevyznal, vzal jsem první knihu, která mi přišla pod ruku - &#8220; PHP - Hypertextový preprocesor&#8221; od Jirku Koska. Už [...]]]></description>
			<content:encoded><![CDATA[<h2>Historie</h2>
<p>Než se dostanu k jádru věci, dovolte mi malou cestu časem.</p>
<p>Někdy kolem roku 1999 jsem se jako první jazyk začal učit PHP. Zadáním bylo vytvoření interního web systému pro hodnocení zaměstnanců. Protože jsem se v jazycích nevyznal, vzal jsem první knihu, která mi přišla pod ruku - &#8220; <a href="http://www.kosek.cz/php/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.kosek.cz');">PHP - Hypertextový preprocesor</a>&#8221; od Jirku Koska. Už tehdy mě překvapila neuvěřitelná rychlost vývoje. Za týden jsem zpatlal první verzi, napojenou na MySQL databázi. Žádné šablony, žádné navrhové vzory, žádné transakce, žádná 3-vrstvá architektura. Logika pěkně zmatlaná s HTML, pár stránek. Autorizace spočívala ve vsunutí kouzelného atributu do HTML requestu s hodnotou 1, což bylo dle mé naivní představy dostatečné.</p>
<p>Jirka Kosek mi pak stačil pro další 3 roky, abych se uživil jako PHP programátor. Postupně jsem se naučil, že je dobré oddělit logiku od aplikační vrstvy, že existuje něco jako &#8220;normální forma&#8221;, &#8220;sql inject&#8221;, &#8220;databázové transakce&#8221;, apod. Měl jsem však pocit, že musí existovat něco &#8220;jednoduššího&#8221;, více výkonného, standardního. Tak jsem se dostal k Javě, která byla v té době (kolem roku 2002) opravdu moc sexy. Pomocí <a href="http://www.netbeans.org" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.netbeans.org');">Netbeans</a> bylo docela rychlé napsat jednoduchou aplikaci ve Swingu přes GUI editor. Co na tom, že metody v kódu byly zamknuté, nedalo se do nich zasáhnout ručně, třída byla pořád delší a Netbeans byly opravdu pomalé. Psal jsem v Javě, takže jsem byl na špičce, to bylo jasné ;).</p>
<p>Od Swingu jsem se chtěl dostat k Java web aplikacím. A tady jsem poprvé narazil. Napsat Java web aplikaci nebylo vůbec jednoduché. Najednou jsem toho měl umět opravdu dost. JSP, JSTL, EJB, JDBC. Samé stupidní zkratky a co bylo nejhorší - pořad přibývaly. Jen jsem se začal zhruba orientovat, co je vlastně JSP, tak přišlo JSTL se sdělením &#8220;jsi idiot, že píšeš v JSP, použij mě&#8221;. Jen co jsem zkusil JDBC, dočetl jsem se &#8220;JDBC je pro lamy, EJB, to je budoucnost&#8221;. Ale protože jsem byl čínský pionýr, který si sám dává překážky, aby je mohl zdolávat, do všech těch buzz jsem se zakousl. Když něco dělám, tak to dělám pořádně a <a href="http://www.hradil.org/certifikace-java-2-platform/" onclick="">tak jsem to vzal a dal včetně certifikace</a>. Alespoň jsem se naučil základy Javy. To mi ovšem na psaní web aplikace nestačilo. Musel jsem projít všemi těmi Sun srandami. Než jsem byl schopen web aplikaci napsat, musel jsem studovat několik měsíců. To mi nevadilo, protože na konci přece čekala nirvána - něco se naučím a pak už budu web aplikace sekat jako Baťa cvičky.</p>
<p>Jenže Java je nevěrná mrcha. Něco se naučíte a hned přijdou další frameworky, které vše řeší lépe a radostněji. Takhle jsem se postupně dostal k ORM (Hibernate, Toplink), dalším  prezentačním frameworkům (JSF, Stripes, Wicket, Spring MVC), must-have nástrojům (Ant, Maven, Spring, JUnit) a dalšímu, občas dobrému balastu (princip nepřetržité integrace, statická analýza kódu, normy psaní zdrojových kódů&#8230;). Tohoto období nelituji, určitě mě neustálé studium posunulo dál. Javu jsem také několik let školil a všechny své kolegy (a později zaměstnance či klienty) dostal, jemně nasměroval či dokopal k Sun certifikacím.</p>
<p>Poslední rok jsem však začal pochybovat ohledně celé Java platformy. Nikoli z pohledu nefunkčnosti, spíše z pohledu efektivity vývoje. V <a href="http://www.kyberie.cz" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.kyberie.cz');">Kyberii</a> používáme striktně agilní vývoj, což je komplikované označení pro &#8220;nevím-co-bude-zítra-vývoj&#8221;. Agilní vývoj (a jeho implementace XP a Scrum) stále považuji za nejefektivnější možný způsob vývoje software. A tady mě Java brzdila.</p>
<p>Největší překážky v Javě při vývoji webových aplikací:</p>
<h2>Složitost jazyka</h2>
<p>Java není jednoduchý jazyk. Ze začátku to vypadá, že ano, ale pár tříd aplikaci nedělá. Po &#8220;hello world&#8221; následuje polymorfismus, modifikátory přístupu, kontejnery, knihovny ze základního API, chuťovky jako reflexe či vlákna&#8230; Jistě, nemusíme znát všechno, ale většinu ze standardního API bychom znát měli. Obecně trvá vyškolení Java začátečníka na programátora, který projde standardní certifikací <a href="http://in.sun.com/training/certification/java/scjp.xml" onclick="javascript:pageTracker._trackPageview('/outbound/article/in.sun.com');">SJCP</a>, asi 20 týdnů (zdroj: moje zkušenosti). Po této době ovšem tento programátor umí jen základy jazyka a není schopen psát produkční aplikaci (nezná ORM, JSP, apod&#8230;). Celý proces vyškolení nováčka do produkčního programátora trvá asi rok. Zkuste si to přepočítat na peníze. To byl IMHO také důvod, proč byli a jsou Java experti přeplacení. Samotný vývoj software je pak pro klienty zbytečně drahý a pokud budeme předpokládat, že Java je trend, pak trh trpí nedostatkem kvalitního software.</p>
<h2>Širokost platformy</h2>
<p>Sun nasměruje budoucí vývoj platformy přes JCP, vznikne nějaké JSR a pak se začne implementovat. Z různých implementací si vývojový tým musí vybrat. A právě možnost výběru vývoj komplikuje. Začínáme pochybovat, zda jsme si vybrali správně, porovnáváme, zkoušíme. U nás jsme třeba začali s JPA přes Toplink, ale později jsme migrovali na Hibernate, protože měl prostě větší komunitní základnu. Byla to jistě správná volba, ale stálo nás to nemalé úsilí a spoustu času.</p>
<h2>Špagetování</h2>
<p>Pokud už vyberete správné frameworky (u nás to byla kombinace Hibernate JPA - Wicket - Maven2 - Spring - Hudson CI - JUnit-Lucene-Hibernate Search), tak celou aplikaci musíte provázat spoustou špaget, aby vám držela pohromadě. Na špagetování se ukázal vhodný Spring, který je pro default použití jednoduchý, ale při bližším koketování <a href="http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/static.springsource.org');">neuvěřitelně složitý</a>. Křivka učení je hlavně ze začátku velmi nestrmá.</p>
<h2>Kompilace a redeploy</h2>
<p>Tohle byla největší překážka. Při každé změně třeba i v prezentační vrstvě jsme museli aplikaci redeployovat. Servlet kontejner (Tomcat nebo pro vývoj Jetty) namísto nabušeného aplikačního serveru (Glassfish, apod.) proces dost urychlil, ale stejně jsme museli čekat typicky desítky vteřin, než se po reloadu stránky změna projevila (počet tříd v naší aplikaci šel do několika stovek či tisíc). Zkoušeli jsme i hot-redeploy, Java Rebel, apod. Ale stejně jsme museli čekat. Se slzami v očích jsem vzpomínal na PHP, které sice bylo vedle Javy jako ošlivý nepříbuzný, ale zato pekelně rychlý.</p>
<h2>Nepřetržitá integrace a testování</h2>
<p>V Javě není na testování ani nepřetržitou integraci žádný standard či implementace, která by proces napříč různě použitými frameworky zjednodušila a urychlila. Všechno jsme museli vybudovat ručně - Maven, Hudson CI, scripty pro naplnění testovací databáze, vyhodnocení, pokrytí testy, posílání mailem, apod. Což jsme zvládli v řádu týdnů, ale samozřejmě jsem se musel ptát - copak to nejde jednodušeji?</p>
<h2>Ruby on Rails - zpátky na koleje</h2>
<p>A ono to jde. Po několika týdnech experimentování, setkání s <a href="http://www.jetminds.com" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.jetminds.com');">Jiřím Fabiánem z JetMinds</a> a vytvoření pilotního projektu jsme na konci roku 2009 z Javy migrovali na <a href="http://rubyonrails.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/rubyonrails.org');">Ruby on Rails</a>. Když jsem viděl, jak rychle jsme dokázali vytvořit nový projekt a udržovat klienty spokojené tím, že nemusí na výsledky čekat hodiny či dny, ale vteřiny či minuty, bylo rozhodnuto. <strong>Efektivita vývoje je dle našich interních měření asi 10x větší.</strong> Vrátili jsme se na začátek. Jen místo PHP teď jedeme v Ruby :).</p>
<p>Ale o tom zase v příštím článku.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hradil.org/java-vs-ruby-on-rails-cesta-tam-a-zase-zpatky/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Hibernate Search – fulltext nad Hibernate ORM</title>
		<link>http://www.hradil.org/hibernate-search-fulltext-nad-hibernate-orm/</link>
		<comments>http://www.hradil.org/hibernate-search-fulltext-nad-hibernate-orm/#comments</comments>
		<pubDate>Sun, 10 May 2009 20:48:08 +0000</pubDate>
		<dc:creator>Jiří Hradil</dc:creator>
		
		<category><![CDATA[hibernatesearch]]></category>

		<guid isPermaLink="false">http://www.hradil.org/?p=175</guid>
		<description><![CDATA[Pokud chceme do naší Java aplikace integrovat fulltextové vyhledávání, dříve či později skončíme u Apache Lucene, frameworku, který je pro vyhledávání v Javě v podstatě standardem a synonymem.  Pokud se rozhodneme používat čistý Lucene, musíme mj. řešit:

mapování doménových objektů do Lucene (v Lucene jsou všechny atributy obyčejné řetězce)
mapování výsledků vyhledávání 	zpátky do doménových objektů
transakce
otevírání [...]]]></description>
			<content:encoded><![CDATA[<p>Pokud chceme do naší Java aplikace integrovat fulltextové vyhledávání, dříve či později skončíme u <a href="http://lucene.apache.org" onclick="javascript:pageTracker._trackPageview('/outbound/article/lucene.apache.org');">Apache Lucene</a>, frameworku, který je pro vyhledávání v Javě v podstatě standardem a synonymem.  Pokud se rozhodneme používat čistý Lucene, musíme mj. řešit:</p>
<ul>
<li>mapování doménových objektů do Lucene (v Lucene jsou všechny atributy obyčejné řetězce)</li>
<li>mapování výsledků vyhledávání 	zpátky do doménových objektů</li>
<li>transakce</li>
<li>otevírání a zavírání indexu</li>
<li>zamykání indexu, v 1 okamžiku může do indexu zapisovat pouze 1 proces</li>
<li>škálování fulltextu přes více serverů</li>
</ul>
<p>Jestliže se však držíme zavedených řešení a používáme <a href="https://www.hibernate.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.hibernate.org');">Hibernate</a> (ať už Hibernate Core nebo Hibernate jako JPA providera), můžeme využít rozšíření Hibernate Search.<br />
<a href="https://www.hibernate.org/410.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.hibernate.org');">Hibernate Search</a> je projekt, který Hibernate Core doplňuje o fulltextové prohledávání persistovaných objektů a funguje jako fasáda nad Lucene.</p>
<p>Hibernate Search nabízí:</p>
<ul>
<li>API nad fulltextem podobné JPA</li>
<li>odstínění od low-level objektů Lucene-nestaráme se vůbec o otevření/uzavření indexu, o zamykání indexu 1 procesem, atd.</li>
<li>fulltextové vyhledávání nad persistovanými objekty pomocí Lucene tříd a objektů</li>
<li>automatický převod objektů do indexu a zpět (pokud nevyhovuje/nepostačuje, lze napsat převodníky)</li>
<li>automatickou synchronizaci indexu s doménovými objekty</li>
<li>reindexaci již persistovaných objektů</li>
<li>výsledky vyhledávání jsou kompletní objekty, Hibernate Search vrátí na dotaz seznam 	vyhovujících ID a Hibernate pak načte z db objekty s těmito 	ID</li>
<li>transakční chování – zápis do fulltext indexu se provádí až nakonec po commitu změn do db</li>
<li>škálování jako master/slave pomocí <a href="http://java.sun.com/products/jms/" onclick="javascript:pageTracker._trackPageview('/outbound/article/java.sun.com');">JMS</a></li>
<li>jednoduchost, pokud známe Lucene a Hibernate, můžeme jej začít používat okamžitě.</li>
</ul>
<h2>Ukázková aplikace</h2>
<p>Pro praktickou ukázku integrujeme fulltextové vyhledávání do jednoduché webové aplikace pro správu firem (klasická create-read-update aplikace).</p>
<h3>Použitý stack</h3>
<h3>
<ul>
<li><span style="font-weight: normal; ">Hibernate Entity Manager 3.4.0.GA - persistence</span></li>
<li><span style="font-weight: normal; ">Hibernate Search 3.1.0.GA – fulltext</span></li>
<li><span style="font-weight: normal; ">Apache Maven 2.0.9 – project management</span></li>
<li><span style="font-weight: normal; ">Apache Wicket 1.3.5 – prezentační vrstva</span></li>
<li><span style="font-weight: normal; ">Spring 2.5.5 – lepidlo</span></li>
<li><span style="font-weight: normal; ">PostgreSQL 8.2-relační databáze</span></li>
</ul>
<p><em>Poznámka k následujícím zdrojovým kódům: uvádím pouze soubory, které se bezprostředně týkají Hibernate Search, kompletní zdrojáky aplikace jsou na konci příspěvku.</em></h3>
<h3>Závislosti</h3>
<p>pom.xml</p>
<pre class="brush: xml;">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd&quot;&gt;

 &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
 &lt;groupId&gt;org.hradil&lt;/groupId&gt;
 &lt;artifactId&gt;HibernateSearchSimpleApplication&lt;/artifactId&gt;
&lt;packaging&gt;war&lt;/packaging&gt;
 &lt;version&gt;1.0&lt;/version&gt;
 &lt;name&gt;Hibernate Search Simple Application&lt;/name&gt;

 &lt;dependencies&gt;

 &lt;!--  wicket --&gt;
 &lt;dependency&gt;
 &lt;groupId&gt;org.apache.wicket&lt;/groupId&gt;
 &lt;artifactId&gt;wicket&lt;/artifactId&gt;
 &lt;version&gt;1.3.5&lt;/version&gt;
 &lt;/dependency&gt;

 &lt;!-- wicket+spring --&gt;
 &lt;dependency&gt;
 &lt;groupId&gt;org.apache.wicket&lt;/groupId&gt;
 &lt;artifactId&gt;wicket-spring-annot&lt;/artifactId&gt;
 &lt;version&gt;1.3.5&lt;/version&gt;
 &lt;/dependency&gt;

 &lt;dependency&gt;
 &lt;groupId&gt;org.hibernate&lt;/groupId&gt;
 &lt;artifactId&gt;hibernate-entitymanager&lt;/artifactId&gt;
 &lt;version&gt;3.4.0.GA&lt;/version&gt;
 &lt;/dependency&gt;

 &lt;dependency&gt;
 &lt;artifactId&gt;hibernate-annotations&lt;/artifactId&gt;
 &lt;groupId&gt;org.hibernate&lt;/groupId&gt;
 &lt;version&gt;3.4.0.GA&lt;/version&gt;
 &lt;/dependency&gt;

 &lt;!-- Hibernate Search --&gt;
 &lt;dependency&gt;
 &lt;groupId&gt;org.hibernate&lt;/groupId&gt;
 &lt;artifactId&gt;hibernate-search&lt;/artifactId&gt;
 &lt;version&gt;3.1.0.GA&lt;/version&gt;
 &lt;/dependency&gt;

 &lt;dependency&gt;
 &lt;groupId&gt;org.springframework&lt;/groupId&gt;
 &lt;artifactId&gt;spring&lt;/artifactId&gt;
 &lt;version&gt;2.5.5&lt;/version&gt;
 &lt;exclusions&gt;
 &lt;exclusion&gt;
 &lt;groupId&gt;commons-logging&lt;/groupId&gt;
 &lt;artifactId&gt;commons-logging&lt;/artifactId&gt;
 &lt;/exclusion&gt;
 &lt;/exclusions&gt;
 &lt;/dependency&gt;

 &lt;!-- LOGGING DEPENDENCIES - LOG4J --&gt;
 &lt;dependency&gt;
 &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
 &lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;
 &lt;version&gt;1.5.2&lt;/version&gt;
 &lt;/dependency&gt;

 &lt;dependency&gt;
 &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
 &lt;artifactId&gt;slf4j-log4j12&lt;/artifactId&gt;
 &lt;version&gt;1.5.2&lt;/version&gt;
 &lt;/dependency&gt;

 &lt;!-- work around for jetty commons logging issue --&gt;
 &lt;dependency&gt;
 &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
 &lt;artifactId&gt;jcl-over-slf4j&lt;/artifactId&gt;
 &lt;version&gt;1.5.2&lt;/version&gt;
 &lt;/dependency&gt;

 &lt;dependency&gt;
 &lt;groupId&gt;c3p0&lt;/groupId&gt;
 &lt;artifactId&gt;c3p0&lt;/artifactId&gt;
 &lt;version&gt;0.9.1.2&lt;/version&gt;
 &lt;/dependency&gt;

 &lt;dependency&gt;
 &lt;groupId&gt;postgresql&lt;/groupId&gt;
 &lt;artifactId&gt;postgresql&lt;/artifactId&gt;
 &lt;version&gt;8.2-507.jdbc3&lt;/version&gt;
 &lt;scope&gt;compile&lt;/scope&gt;
 &lt;/dependency&gt;

 &lt;!--  JUNIT DEPENDENCY FOR TESTING --&gt;
 &lt;dependency&gt;
 &lt;groupId&gt;junit&lt;/groupId&gt;
 &lt;artifactId&gt;junit&lt;/artifactId&gt;
 &lt;version&gt;3.8.2&lt;/version&gt;
 &lt;scope&gt;test&lt;/scope&gt;
 &lt;/dependency&gt;

 &lt;!--  JETTY DEPENDENCIES FOR TESTING  --&gt;

 &lt;dependency&gt;
 &lt;groupId&gt;org.mortbay.jetty&lt;/groupId&gt;
 &lt;artifactId&gt;jetty&lt;/artifactId&gt;
 &lt;version&gt;6.1.4&lt;/version&gt;
 &lt;scope&gt;provided&lt;/scope&gt;
 &lt;/dependency&gt;

 &lt;/dependencies&gt;

 &lt;build&gt;
 &lt;resources&gt;
 &lt;resource&gt;
 &lt;directory&gt;src/main/resources&lt;/directory&gt;
 &lt;/resource&gt;
 &lt;resource&gt;
 &lt;directory&gt;src/main/java&lt;/directory&gt;
 &lt;includes&gt;
 &lt;include&gt;**&lt;/include&gt;
 &lt;/includes&gt;
 &lt;excludes&gt;
 &lt;exclude&gt;**/*.java&lt;/exclude&gt;
 &lt;/excludes&gt;
 &lt;/resource&gt;
 &lt;/resources&gt;
 &lt;testResources&gt;
 &lt;testResource&gt;
 &lt;directory&gt;src/test/java&lt;/directory&gt;
 &lt;includes&gt;
 &lt;include&gt;**&lt;/include&gt;
 &lt;/includes&gt;
 &lt;excludes&gt;
 &lt;exclude&gt;**/*.java&lt;/exclude&gt;
 &lt;/excludes&gt;
 &lt;/testResource&gt;
 &lt;/testResources&gt;
&lt;plugins&gt;
&lt;plugin&gt;
 &lt;groupId&gt;org.mortbay.jetty&lt;/groupId&gt;
 &lt;artifactId&gt;maven-jetty-plugin&lt;/artifactId&gt;

 &lt;configuration&gt;
 &lt;scanIntervalSeconds&gt;5&lt;/scanIntervalSeconds&gt;
 &lt;contextPath&gt;/&lt;/contextPath&gt;
 &lt;/configuration&gt;

 &lt;/plugin&gt;
&lt;plugin&gt;
 &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
 &lt;version&gt;2.0.2&lt;/version&gt;
 &lt;configuration&gt;
 &lt;source&gt;1.6&lt;/source&gt;
 &lt;target&gt;1.6&lt;/target&gt;
 &lt;/configuration&gt;
 &lt;/plugin&gt;

 &lt;/plugins&gt;
 &lt;/build&gt;

&lt;/project&gt;
</pre>
<h3>Konfigurace</h3>
<p>applicationContext-jpa.xml</p>
<pre class="brush: xml;">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
 xmlns:tx=&quot;http://www.springframework.org/schema/tx&quot;
 xsi:schemaLocation=&quot;
 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd&quot;
 &gt;

 &lt;description&gt;
 Konfigurace kontextu pro JPA, vcetne datovych zdroju a transakcnich manazeru.
 &lt;/description&gt;

 &lt;bean id=&quot;dataSource&quot; class=&quot;com.mchange.v2.c3p0.ComboPooledDataSource&quot; destroy-method=&quot;close&quot;&gt;
 &lt;description&gt;
 Datovy zdroj pro persistentni vrstvu. Obsahuje udaje o pripojeni k databazi.
 &lt;/description&gt;
&lt;property name=&quot;driverClass&quot; value=&quot;org.postgresql.Driver&quot;/&gt;
&lt;property name=&quot;jdbcUrl&quot; value=&quot;jdbc:postgresql://localhost/HibernateSearchSimple&quot;/&gt;
&lt;property name=&quot;user&quot; value=&quot;postgres&quot;/&gt;
&lt;property name=&quot;password&quot; value=&quot;&quot;/&gt;
 &lt;/bean&gt;

 &lt;bean id=&quot;entityManagerFactory&quot; class=&quot;org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean&quot;&gt;
 &lt;description&gt;
 Tovarna pro manazer entit. Je pouzita trida LocalContainerEntityManagerFactoryBean,
 ktera je doporucena pro produkcni nasazeni JPA.
 Viz. http://static.springframework.org/spring/docs/2.5.x/reference/orm.html
 &lt;/description&gt;
&lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;/&gt;
&lt;property name=&quot;jpaVendorAdapter&quot;&gt;
 &lt;bean class=&quot;org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter&quot;&gt;
&lt;property name=&quot;databasePlatform&quot; value=&quot;org.hibernate.dialect.PostgreSQLDialect&quot; /&gt;
&lt;property name=&quot;generateDdl&quot; value=&quot;false&quot;/&gt;
 &lt;/bean&gt;
 &lt;/property&gt;

 &lt;!-- nastaveni JPA a Hibernate Search --&gt;
&lt;property name=&quot;jpaProperties&quot;&gt;

 &lt;value&gt;

 # konfigurace JPA
 hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
 hibernate.hbm2ddl.auto=validate

 # debugging / logging
 hibernate.show_sql=true
 hibernate.format_sql=true
 hibernate.use_sql_comments=true

 # konfigurace Hibernate Search
 # kde bude ulozen Lucene index
 hibernate.search.default.indexBase=/tmp/index

 &lt;/value&gt;

 &lt;/property&gt;
 &lt;/bean&gt;

 &lt;tx:annotation-driven transaction-manager=&quot;transactionManager&quot;/&gt;

 &lt;!-- transakcni manazer --&gt;
 &lt;bean id=&quot;transactionManager&quot; class=&quot;org.springframework.orm.jpa.JpaTransactionManager&quot;&gt;
&lt;property name=&quot;entityManagerFactory&quot; ref=&quot;entityManagerFactory&quot;/&gt;
 &lt;/bean&gt;

&lt;/beans&gt;
</pre>
<h3>Entita</h3>
<p>Company.java</p>
<pre class="brush: java;">
package org.hradil.search.entity;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import org.hibernate.search.annotations.DocumentId;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;

/**
 * Entita reprezentuje firmu.
 * Firmu lze ve fulltextu vyhledat podle id, name a regNo.
 *
 * @author jirka@hradil.org
 */
@Entity
@Table(name = &quot;company&quot;)
@Indexed //tridu budeme chtit indexovat ve fulltextu
public class Company implements Serializable {

 private static final long serialVersionUID = 1216348069826762176L;

 @Id
 @Column(name = &quot;id&quot;)
 @SequenceGenerator(name = &quot;company_id_seq&quot;, sequenceName = &quot;company_id_seq&quot;)
 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = &quot;company_id_seq&quot;)
 @DocumentId //primarni klic objektu ve fulltextu, zaroven podle nej muzeme vyhledavat
 private int id;

 @Column
 @Field //ve fulltextu chceme hledat firmu podle nazvu
 private String name;

 @Column
 @Field //ve fulltextu chceme hledat firmu podle IC
 private String regNo;

 /**
 * Vytvori novou firmu.
 */
 public Company() {
 }

 /**
 * Vytvori novou firmu a predvyplni atributy.
 * @param name nazev firmy
 * @param regNo IC
 */
 public Company(final String name, final String regNo) {
 this.name = name;
 this.regNo = regNo;
 }

 /**
 * Vrati id firmy
 * @return id
 */
 public int getId() {
 return id;
 }

 /**
 * Nastavi id firmy
 * @param id id
 */
 public void setId(int id) {
 this.id = id;
 }

 /**
 * Vrati nazev firmy.
 * @return nazev
 */
 public String getName() {
 return name;
 }

 /**
 * Nastavi nazev firmy.
 * @param name nazev
 */
 public void setName(String name) {
 this.name = name;
 }

 /**
 * Vrati IC firmy.
 * @return IC
 */
 public String getRegNo() {
 return regNo;
 }

 /**
 * Nastavi IC firmy.
 * @param regNo IC
 */
 public void setRegNo(String regNo) {
 this.regNo = regNo;
 }
}
</pre>
<h3>Servisní vrstva - uložení, úprava, vyhledání</h3>
<p>CompanyServiceImpl.java</p>
<pre class="brush: java;">
package org.hradil.search.service;

import org.hradil.search.entity.Company;

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.hibernate.search.jpa.FullTextQuery;
import org.hibernate.search.jpa.Search;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
 * Implementace sluzby pro firmu.
 *
 * @author jirka@hradil.org
 */
@Transactional(propagation = Propagation.REQUIRED)
@Service
public class CompanyServiceImpl implements CompanyService {

 @PersistenceContext
 private EntityManager em;

 /**
 * {@inheritDoc}
 */
 @Override
 public void addCompany(Company newCompany) {
 em.persist(newCompany);
 }

 /**
 * {@inheritDoc}
 */
 @Override
 @SuppressWarnings(&quot;unchecked&quot;)
 @Transactional(readOnly = true)
 public List&lt;Company&gt; findCompanyBy(final String fulltextQuery) {

 Assert.notNull(fulltextQuery, &quot;retezec pro vyhledavani nesmi byt null!&quot;);

 //vezmeme instanci manazeru fulltextu
 FullTextEntityManager ftEm = Search.getFullTextEntityManager(em);

 //vytvorime parser pro prohledavane atributy firmy, pouzijeme standardni analyzer
 QueryParser parser = new MultiFieldQueryParser(new String[]{&quot;id&quot;, &quot;name&quot;, &quot;regNo&quot;}, new StandardAnalyzer());

 //vytvorime dotaz do Lucene
 org.apache.lucene.search.Query luceneQuery;

 //pokud po zruseni vsech bilych znaku a hvezdicek zustane jen prazdny retezec, pak vracime vsechny zaznamy
 //napr. dotaz &quot;**** * **  *&quot; bude vyhodnocen tak, ze chceme prohledavat vsechny firmy
 if (StringUtils.trimAllWhitespace(StringUtils.deleteAny(fulltextQuery, &quot;*&quot;)).isEmpty()) {
 luceneQuery = new MatchAllDocsQuery();
 } else { //byl zadan retezec, vyhledavame
 try {
 luceneQuery = parser.parse(fulltextQuery); //zparsujeme predany dotaz pomoci parseru Lucene
 } catch (ParseException e) { //neplatny dotaz, prekonverujeme na runtime vyjimku, nemusime zachytavat
 throw new RuntimeException(&quot;Neplatny dotaz do fulltextu: &quot; + fulltextQuery, e);
 }
 }

 //vytvorime normalni JPA dotaz, ale pres rozhrani fulltextu
 FullTextQuery query = ftEm.createFullTextQuery(
 luceneQuery,
 Company.class);

 //tridime dle relevance DESC, pote dle id DESC
 SortField[] sortFields = new SortField[2];
 sortFields[0] = SortField.FIELD_SCORE; //relevance, default DESC
 sortFields[1] = new SortField(&quot;id&quot;, SortField.INT, true); //id, je to intener, DESC=true
 Sort sort = new Sort(sortFields);

 //pridame trideni do dotazu
 query.setSort(sort);

 //a vratime rovnou vyhovujici seznam firem z ORM
 return query.getResultList();

 }

 /**
 * {@inheritDoc}
 */
 @Override
 @SuppressWarnings(&quot;unchecked&quot;)
 @Transactional(readOnly = true)
 public Company findCompanyBy(int id) {
 return em.find(Company.class, id);
 }

 @Override
 @Transactional(readOnly = false)
 public void updateCompany(Company company) {
 em.merge(company);
 }
}
</pre>
<h3>Zdrojové kódy aplikace</h3>
<p><a href="http://www.hradil.org/files/hibernatesearchsimple.zip" onclick="javascript:pageTracker._trackPageview('/downloads/files/hibernatesearchsimple.zip');">hibernatesearchsimple</a></p>
<p>Konfiguraci a rozjetí v clusteru si ukážeme v některém dalším příspěvku.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hradil.org/hibernate-search-fulltext-nad-hibernate-orm/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Levná hardwarová infrastruktura</title>
		<link>http://www.hradil.org/levna-hardwarova-infrastruktura/</link>
		<comments>http://www.hradil.org/levna-hardwarova-infrastruktura/#comments</comments>
		<pubDate>Tue, 21 Apr 2009 21:05:10 +0000</pubDate>
		<dc:creator>Jiří Hradil</dc:creator>
		
		<category><![CDATA[hardware]]></category>

		<guid isPermaLink="false">http://www.hradil.org/?p=168</guid>
		<description><![CDATA[Pro jednoho z našich klientů navrhujeme HW infrastrukturu a vzhledem k přemrštěným cenám za značkové servery známých výrobců (IBM, Sun, HP&#8230;) padnul návrh na vybudování clusteru pro aplikační kontejnery z &#8220;neznačkových&#8221; desktopů, které bychom použili jako servery.  Myšlenka má původ v Google (zdroj: David A. Vise, Mark Malseed: Google Story, Pragma 2007, ISBN: 978-80-7349-034-8), kde [...]]]></description>
			<content:encoded><![CDATA[<p>Pro jednoho z našich klientů navrhujeme HW infrastrukturu a vzhledem k přemrštěným cenám za značkové servery známých výrobců (IBM, Sun, HP&#8230;) padnul návrh na vybudování clusteru pro aplikační kontejnery z &#8220;neznačkových&#8221; desktopů, které bychom použili jako servery.  Myšlenka má původ v <a href="http://www.google.com" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.google.com');">Google</a> (<em>zdroj: David A. Vise, Mark Malseed: Google Story, Pragma 2007, ISBN: 978-80-7349-034-8</em>), kde je nestabilita desktopů vyvážena jejich množstvím. Pokud některý z  desktopů-nodů vypadne,  zafunguje <a href="http://en.wikipedia.org/wiki/Failover" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');">fail-over</a> a <a href="http://en.wikipedia.org/wiki/Load_balancer" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');">load balancer</a> na něj přestane posílat požadavky. Klient v nejhorším případě zaznamená výpadek v řádu vteřin.</p>
<p><strong>Klady řešení:</strong></p>
<ul>
<li>cena, pokud bychom desktop=nod poskládali ze značkových komponent, pak je cena jednoho tohoto nodu v řádech tisíců</li>
<li>jednoduchý upgrade - nod by mohl být při výpadku či upgrade nahrazen rychlejším, poskládaným z aktuálních &#8220;best of breed&#8221; komponent</li>
<li>dostupnost - nod lze poskládat z komponent, nakoupených v jakémkoli supermarketu společně s rohlíky</li>
<li>nezávislost na dodavateli - sbohem obchodníkům, prodávajícím řešení, kterým nerozumí nebo na kterých se chtějí napakovat <img src='http://www.hradil.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </li>
</ul>
<p><strong>Zápory řešení:</strong></p>
<ul>
<li>možná nekompatibilita komponent - paměť od výrobce ABC  si nerozumí s deskou výrobce XYZ, což nemusíme poznat okamžitě, nod může vykazovat nestabilitu náhodně, lze řešit intenzivním testováním</li>
<li>poskytování <a href="http://en.wikipedia.org/wiki/Service_level_agreement" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');">SLA</a> - infrastrukturu musíme podporovat sami, ideálně mít nakoupeno několik desktopů do zásoby a při výpadku nod rovnou vyměnit za jiný</li>
<li>požadavky na prostor - pokud by nod byl umístěn v běžném mini či miditoweru, zabírá více prostoru, než hezké 1U či 2U skříně &#8220;značkových&#8221; výrobců. Nicméně desktopy lze do skříní montovat taky.</li>
</ul>
<p><strong>Co je třeba dále zvážit:</strong></p>
<ul>
<li>pozor na<a href="http://en.wikipedia.org/wiki/Single_point_of_failure" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"> SPOF (single point of failure)</a>, pokud např. máme relační databázi, která fail-over na jiný server neumí, potencionálně nestabilní desktop tady nelze použít</li>
<li>disky použité v nodu, protože na nod bude deploynutý jen .war aplikace a relační databáze je umístěna v jiné vrstvě, stačí nám levné disky, zapojené do prostého <a href="http://en.wikipedia.org/wiki/Raid_1#RAID_1" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');">RAID 1 (mirror)</a>, abychom při výpadku jednoho disku neohrozili stabilitu aplikace</li>
</ul>
<p><strong>Použité technologie:</strong></p>
<ul>
<li>Platforma - samozřejmě <a href="http://java.com" onclick="javascript:pageTracker._trackPageview('/outbound/article/java.com');">Java</a></li>
<li>Aplikační/servlet kontejner - <a href="http://tomcat.apache.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/tomcat.apache.org');">Apache Tomcat</a></li>
<li>ORM - <a href="http://www.hibernate.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.hibernate.org');">Hibernate</a></li>
<li>Prezentační vrstva  - <a href="http://java.sun.com/products/jsp/" onclick="javascript:pageTracker._trackPageview('/outbound/article/java.sun.com');">JSP</a>, <a href="http://java.sun.com/products/servlet/" onclick="javascript:pageTracker._trackPageview('/outbound/article/java.sun.com');">Servlety</a>, <a href="http://wicket.apache.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/wicket.apache.org');">Apache Wicket</a></li>
<li>Dependency injection a lepidlo - <a href="http://www.springsource.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.springsource.org');">Spring</a></li>
<li>Škálování, fail-over na aplikační vrstvě - <a href="http://terracotta.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/terracotta.org');">Terracotta</a>, tedy sdílená VM</li>
</ul>
<p><span style="color: #ff0000;"><strong>Použil jste někdo podobnou infrastrukturu v ostrém provozu?</strong></span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.hradil.org/levna-hardwarova-infrastruktura/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Definitivně: Oracle kupuje Sun</title>
		<link>http://www.hradil.org/definitivne-oracle-kupuje-sun/</link>
		<comments>http://www.hradil.org/definitivne-oracle-kupuje-sun/#comments</comments>
		<pubDate>Mon, 20 Apr 2009 20:33:34 +0000</pubDate>
		<dc:creator>Jiří Hradil</dc:creator>
		
		<category><![CDATA[Nezařazené]]></category>

		<guid isPermaLink="false">http://www.hradil.org/?p=163</guid>
		<description><![CDATA[Aktuální informace přímo od zdroje - Oracle a Sun Microsystems vstupují do konečné fáze dohody, podle které Oracle koupí Sun za cca 7.4 miliardy dolarů. Oracle tak získává Javu, Solaris a ostatní technologie (zdroj: Sun Microsystems: Oracle to buy Sun). Vedle nezajímavých řečí o 20-letém partnersví a obrovském přínosu pro komunitu, uživatele, atd. bude velmi [...]]]></description>
			<content:encoded><![CDATA[<p>Aktuální informace přímo od zdroje - Oracle a Sun Microsystems vstupují do konečné fáze dohody, podle které Oracle koupí Sun za cca 7.4 miliardy dolarů. Oracle tak získává Javu, Solaris a ostatní technologie (zdroj: <a href="http://www.sun.com/third-party/global/oracle/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.sun.com');">Sun Microsystems: Oracle to buy Sun</a>). Vedle nezajímavých řečí o 20-letém partnersví a obrovském přínosu pro komunitu, uživatele, atd. bude velmi zajímavé sledovat skutečný důsledek této akvizice.</p>
<p>Sun byl sice skvělý technologický inovátor, ale svým technologiím nedokázal přinést vhodný obchodní model. Takže se necháme překvapit, co Oracle konkrétně s Javou provede. Co myslíte?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.hradil.org/definitivne-oracle-kupuje-sun/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Postřehy z agilní praxe</title>
		<link>http://www.hradil.org/postrehy-z-agilni-praxe/</link>
		<comments>http://www.hradil.org/postrehy-z-agilni-praxe/#comments</comments>
		<pubDate>Sun, 12 Apr 2009 21:43:33 +0000</pubDate>
		<dc:creator>Jiří Hradil</dc:creator>
		
		<category><![CDATA[Nezařazené]]></category>

		<guid isPermaLink="false">http://www.hradil.org/?p=152</guid>
		<description><![CDATA[Proces vývoje software odráží několik posledních let jasný trend - nestálost prostředí a připravenost na změny. Pokud shrneme několik agilních metodik (extrémní programování, Scrum, RUP), tak řeší v podstatě naprosto stejné věci-krátké iterace, testy, otevřenost, zeptej se kódu, atd.  Některé z těchto pravidel rozeberu.
Vyvíjíme po malých částech, čili v iteracích=cyklech=sprintech. Jedním z důvodů je nestálost [...]]]></description>
			<content:encoded><![CDATA[<p>Proces vývoje software odráží několik posledních let jasný trend - nestálost prostředí a připravenost na změny. Pokud shrneme několik agilních metodik (extrémní programování, Scrum, RUP), tak řeší v podstatě naprosto stejné věci-krátké iterace, testy, otevřenost, zeptej se kódu, atd.  Některé z těchto pravidel rozeberu.</p>
<p><strong>Vyvíjíme po malých částech</strong>, čili v iteracích=cyklech=sprintech. Jedním z důvodů je nestálost prostředí, ve kterém má být software používán (např. situace na trhu či legislativa), ale hlavně neschopnost a nemožnost vyprodukovat stabilní analýzu systému, podle které se dá programovat. Analytiky software považuji za dinosaury, jejichž doba dávno skončila a při vývoji software jednoduše nejsou potřeba. Základ software z pohledu uživatele má navrhnout klient a proveditelnost návrhu určují koneční vývojáři. Setkal jsem se s tím, že analytik (který nikdy neprogramoval), vygeneroval stovky stránek krásných diagramů a dokumentů, které si nechal nebohým klientem schválit. Klient samozřejmě netušil, co ty obrázky znamenají (když ony byly tak hezky barevné&#8230;). Poté se tento stoh dokumentů předhodil vývojářům, kteří obrázkům taky nerozumněli, ale dostali jasný pokyn - &#8220;programujte!&#8221; Po několikaměsíčním vývoji při prezentaci systému klient zjistil, že vlastně teď potřebuje už něco úplně jiného a software se do produkce vůbec nedostal.</p>
<p>Doporučení:</p>
<ul>
<li> iterace je dlouhá max. 1 měsíc, v počátcích vývoje kratší, klidně i 1 týden</li>
<li> vývojáře musíme protlačit ke klientovi, protože jedině zadavatel a tvůrce systému umí pokládat správné otázky a generovat správné odpovědi</li>
<li> po každé iteraci následuje prezentace klientovi, který potvrdí aktuální verzi a na jejím základě generuje požadavky další</li>
<li> popis systému je obsažen ve zdrojovém kódu a v uživatelských zadáních</li>
<li> podrobná dokumentace je zbytečná - vývojáři ji nepotřebují, tvůrce ji musí aktualizovat a koneční uživatelé jsou tak frustrovaní množstvím software, který musí ovládat, že ji číst nebudou. Složitá sice může být problémová doména, kterou software řeší (třeba statistický software), ale nikdy nesmí být složitý software samotný. Pokud např. posadíme statistika k našemu statistickému softwaru, musí ho být schopen do několika minut bez problémů ovládat, protože ovládá problémovou doménu, do které je náš software zasazen a kopíruje ji.</li>
</ul>
<p><strong>Testujeme</strong> - test simuluje používání systému uživatelem. Ať už to schováno pod pojmy test jednotkový (na úrovni metody), integrační (na úrovni větší části systému), zátěžový, opičí, či jiný, smyslem testu je software nastartovat, aby ukázal, co umí. Testy se mají psát průběžně a to z toho důvodu, že na konci je už nikdo psát nebude. Stejně tak s každým testem vytvoříme nekonečného automatického robota, který nám bude hlásit, jestli pořád software dělá, co dělat má. Psaní testů z krátkodobého pohledu zdržuje, ale dlouhodobě nám právě testy drží software stabilní.</p>
<p>Doporučení:</p>
<ul>
<li>kontrolovat pokrytí software testy (např. v Javě přes <a title="Cobertura" href="http://cobertura.sourceforge.net/" onclick="javascript:pageTracker._trackPageview('/outbound/article/cobertura.sourceforge.net');">Cobertura</a>), stanovit si hranici, pod kterou nesmíme jít, ideálně nad 80%)</li>
<li>maximálně urychlit psaní a spouštění testů (<a href="http://en.wikipedia.org/wiki/Mock_object" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');">mock objekty</a>, databáze v paměti)</li>
<li>nepřetržitě integrujeme pomocí integračního serveru (<a title="Continuum" href="http://continuum.apache.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/continuum.apache.org');">Continuum</a>, <a title="Hudson" href="https://hudson.dev.java.net/" onclick="javascript:pageTracker._trackPageview('/outbound/article/hudson.dev.java.net');">Hudson</a>, <a title="Bamboo" href="http://www.atlassian.com/software/bamboo/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.atlassian.com');">Bamboo</a>&#8230;)</li>
</ul>
<p><strong>Jsme malí</strong> - neudržujme velké týmy. Čím více vývojářů, tím více tření, komunikace, rozhodování, administrativy a neefektivity, kterou nakonec (zbytečně) platí klient.</p>
<p>Doporučení:</p>
<ul>
<li>ideální velikost týmu je 3-5 vývojářů, pokud se jich vývoje účastní více, uděláme více týmů a každý z nich má svou problémovou doménu. Tyto týmy však musí být vzájemně synchronizovány, např. pomocí scrum of scrums-porad, kterých se účastní pouze vyhrazený člověk z každého týmu, nikoli všichni členové  dohromady.</li>
</ul>
<p><strong>Posaďme tým dohromady</strong>, čili  &#8220;seat the team together&#8221;. Tým musí sedět pohromadě. Tečka. Tohle je nejrychlejší způsob řešení komplikací během vývoje a udržení synchronizovaných znalostí o systému. Pokud má kdokoli v týmu otázku, může ji položit a dostane se mu okamžité odpovědi.</p>
<p>Doporučení:</p>
<ul>
<li>respektovat klid pro práci - pokud máme otázku, nekřičíme na celou místnost, ale zeptáme se, zda můžeme vyrušit, v extrémních případech si zavedeme každou hodinu několik minut na otázky a odpovědi</li>
<li>červená čepice - jednoduché pravidlo - v týmu je k dispozici symbol &#8220;červené čepice&#8221; (ať už cedulka, nebo opravdová kšiltovka), kdo ji má na hlavě, ten nesmí být rušen</li>
<li>nepracovat z domova přes Internet, bohužel, zatím se ukazuje, že je stále efektivnější být fyzicky pohromadě v 1 místnosti, než komunikovat přes Skype, Jabber, atd. <span style="text-decoration: underline;">Budu rád, když mi tuto neefektivitu někdo vyvrátí a přidá praktické zkušenosti.</span></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.hradil.org/postrehy-z-agilni-praxe/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
