V druhém dílu našeho miniseriálu rozšíříme znalosti frameworku Tapestry 5, zaměříme se na vytvoření jednoduchého formuláře a jeho uložení pomocí OR nástroje Hibernate.

V druhém dílu našeho miniseriálu rozšíříme znalosti frameworku Tapestry 5. Protože předpokládám, že žádná netriviální webová aplikace se neobejde bez přístupu do databáze, tak se podíváme jak připojit Tapestry aplikaci pomocí OR nástroje Hibernate, dále si zkusíme vytvořit jednoduchý formulář a poté pomocí Hibernate uložit jeho obsah do databáze.

Z minulého dílu už máme vytvořený základ aplikace. Tedy budeme rozšiřovat a zkusíme do aplikace přidat jeden formulář, pomocí kterého budeme vytvářet uživatele u jednoduchého knihovnického systému (tedy uživatele budou mít roli knihovníka a čtenáře).

Upravíme vytvořený Hello.tml a vložíme do něj následující řádek.



<a t:type="PageLink" t:page="CreateUser">${message:addForm}</a>


Tento tag nám zobrazí odkaz na controller s názvem CreateUser. ${message:addForm} dává tušit, že půjde o dodanou zprávu. Kde se ovšem berou? Zprávy se čtou z .properties souborů, které jsou umístěny ve stejném souboru jako třída které se týkají a jmenují se stejně. V našem případě vytvoříme soubor/src/cz/morosystems/Tapestry5/pages/Hello.properties a do něho umístíme zprávu:



addForm=Přidat uživatele


Nyní se pustíme do konfigurace aplikace pro použití s Hibernate.

Nejprve budeme potřebovat dohrát do aplikace knihovny, z Tapestry to budou následující

  • tapestry-hibernate-5.1.0.5.jar
  • tapestry-hibernate-core-5.1.0.5.jar
  • hibernate-c3p0-3.3.1.GA.jar
  • commons-codec-1.3.jar

Dále stáhneme distribuci Hibernate. Z Hibrenate Core přidáme k aplikaci následující knihovny:

  • hibernate3.jar
  • commons-collections-3.1.jar
  • jta-1.1.jar

A z Hibernate Annotation

  • hibernate-annotations.jar
  • ejb3-persistence.jar
  • dom4j.jar
  • hibernate-commons-annotations.jar

Dále dodáme knihovny pro práci s datem, tedy Joda Time. Dobré důvody tohoto počínání se můžeme dočíst na blogu Honzy Novotného. Přidáme tedy tyto knihovny:

  • joda-time-1.6.jar
  • joda-time-hibernate-1.1.jar

Nesmíme samozřejmě zapomenout na JDBC driver. Já používám PostgreSQL, tedy přidám driver

  • postgresql-8.3-604.jdbc3.jar

Nyní již můžeme vytvořit již zmíněnou obsluhující třídu CreateUser.



package cz.morosystems.tapestry5.pages;

import org.apache.tapestry5.annotations.InjectPage;

import org.apache.tapestry5.annotations.Property;

import org.apache.tapestry5.hibernate.annotations.CommitAfter;

import org.apache.tapestry5.ioc.annotations.Inject;

import org.hibernate.Session;

import cz.morosystems.hibernate.entities.User;

public class CreateUser {

@Property

private User user;

@Inject

private Session session;

@InjectPage

private Hello hello;

@CommitAfter

Object onSuccess() {

session.persist(user);

return hello;

}

}


Samozřejmě takováto práce s entitami není nejčistší, ale pro názornost to v tomto tutoriálu stačí.

Nyní si ukážeme třídu User, kromě Hibernate anotací si můžeme všimnout anotací Tapestry, jsou to

  • @NonVisual která určuje, že daný atribut se nebude zobrazovat ve formuláři
  • @Validate určuje validaci, která je prováděna pomocí JS

package cz.morosystems.hibernate.entities;

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.apache.tapestry5.beaneditor.NonVisual;
import org.apache.tapestry5.beaneditor.Validate;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;
import org.joda.time.DateTime;
import org.joda.time.contrib.hibernate.PersistentDateTime;

@Entity
@SequenceGenerator(name = "user_id_sequence", sequenceName = "user_id_sequence", initialValue = 1)
@Table(name = "users", schema = "public")
@TypeDefs( { @TypeDef(name = "jodaDateTime", typeClass = PersistentDateTime.class) })
public class User {

private Long id;
private String username;
private String name;
private String password;
private String email;
private DateTime created = new DateTime();
private DateTime changed;
private Role role;

public enum Role {
reader, librarian
}
/**
* @return the id
*/
@NonVisual
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_id_sequence")
public Long getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(Long id) {
this.id = id;
}
/**
* @return the username
*/
@Validate("required")
@Column(name = "username", length = 255)
public String getUsername() {
return username;
}
/**
* @param username the username to set
*/
public void setUsername(String username) {
this.username = username;
}
/**
* @return the name
*/
@Validate("required")
@Column(name = "name", length = 255)
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the password
*/
@Validate("required")
@Column(name = "password", length = 255)
public String getPassword() {
return password;
}
/**
* @param password the password to set
*/
public void setPassword(String password) {
this.password = password;
}
/**
* @return the email
*/
@Validate("required,email")
@Column(name = "email", length = 255)
public String getEmail() {
return email;
}
/**
* @param email the email to set
*/
public void setEmail(String email) {
this.email = email;
}
/**
* @return the created
*/
@Column(name = "created")
@Type(type="org.joda.time.contrib.hibernate.PersistentDateTime")
public DateTime getCreated() {
return created;
}
/**
* @param created the created to set
*/
public void setCreated(DateTime created) {
this.created = created;
}
/**
* @return the changed
*/
@Column(name = "changed")
@Type(type="org.joda.time.contrib.hibernate.PersistentDateTime")
public DateTime getChanged() {
return changed;
}
/**
* @param changed the changed to set
*/
public void setChanged(DateTime changed) {
this.changed = changed;
}
/**
* @return the role
*/
@Validate("required")
@Column(name = "role", length = 10)
public Role getRole() {
return role;
}
/**
* @param role the role to set
*/
public void setRole(Role role) {
this.role = role;
}

}


Nyní již máme vše připraveno pro vytvoření šablony. Vytvoření formuláře je za pomocí tagů. Bude zobrazen klasický formulář s odesílacím tlačítkem, pořadí řádků v šabloně je dané pořadím v User.java.


<t :beaneditform object="user" />

My si jej ovšem můžeme upravit. Například nastavením zobrazovaných názvů. Stejně si můžeme nadefinovat zprávu u odesílacího tlačítka. A názvy zobrazované u jednotlivých rolí. Vše si nadefinujeme v .properties souboru:


username-label=Uživatelské jméno
name-label=Jméno
password-label=Heslo
email-label=Email
role-label=Pozice

reader=Čtenář
librarian=Knihovník

submit-label=Vytvořit


Nyní si ještě upravíme šablonu, aby se u hesla nezobrazoval text a vložíme úpravu pro zobrazení vlastního názvu u tlačítka submit:


<t :beaneditform submitlabel="message:submit-label" object="user">
</t><t :parameter name="password">
<t :label for="password"/>
<t :passwordfield t:id="password" value="user.password"/>
</t>


Ještě konfigurace hibernate, do adresáře /config přidáme soubor hibernate.cfg.xml. V něm nastavíme připojení. Jen pro názornost přikládám můj.


< ?xml version="1.0"?>
< !DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate -configuration>
<session -factory>
<!-- Database connection settings -->
<property name="connection.driver_class">org.postgresql.Driver</property>
<property name="connection.url">jdbc:postgresql://localhost:5432/tapestry</property>
<property name="connection.username">michal</property>
<property name="connection.password">michal</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">none</property>
<mapping class="cz.morosystems.hibernate.entities.User"/>
</session>
</hibernate>


Pouze už stačí aplikaci zbuildovat a spustit. Výsledek i s ukázkou JS validace si můžeme prohlédnout na přiloženém obrázku.

Formulář ukazující automatickou validaci vložených dat
Formulář ukazující automatickou validaci vložených dat