niedziela, 20 czerwca 2010

Layout w JSP

Jeśli zaczynasz tworzyć własną aplikację z użyciem JSP na pewno warto pomyśleć nad szablonami. Jeśli jakieś strony wykorzystują ten sam układ, to samo menu, stopkę, itd. to warto poświęcić trochę czasu na stworzenie oddzielnych plików w których będziemy umieszczać wspólne kawałki kodu.

Prosta konfiguracja web.xml
Zacznijmy od modyfikacji pliku web.xml. Umieszczamy w nim fragment kodu który pozwoli nam używać plików .jspx i .tagx.
<context-param>
 <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
 <param-value>.jspx</param-value>
</context-param>

Pliku tagx
Nasz szablon będzie prostą biblioteką znaczników (taglib), która będzie zawierała podstawowy układ widoku. W folderze WEB-INF tworzymy folder tags a w nim folder gdzie będziemy trzymać nasze szablony. U mnie folder z szablonami będzie nazywał się layout. Następnie tworzymy prosty plik main.tagx w którym będzie znajdował się główny układ naszej aplikacji.
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns:jsp="http://java.sun.com/JSP/Page"> 
 
 <jsp:output doctype-root-element="html"
  doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" />
 <jsp:directive.attribute name="title" required="true"/> 
 
 <head>
     <title>${title}</title>
 </head>
 
 <body> 
  <jsp:directive.include file="/include/menu.jsp" />

  <div style="clear: both;">Layout góra</div>
   <jsp:doBody />  
   <div style="clear: both;">Layout dół</div>    
 </body>
</html>
Plik z szablonem wygląda na standardową stronę jsp. Dzięki elementowi jsp:directive.attribute możemy przekazywać do naszego szablonu różnego rodzaju atrybuty, takie jak tytuł strony. Tworząc atrybut ustawiamy nazwę (name) oraz definiujemy czy atrybut jest wymagany (required). Do atrybutu odwołujemy się poprzez ${NAZWA_ATRYBUTU}. W powyższym przykładzie tworzony jest atrybut title który określa tytuł strony (atrybut użyty jest w sekcji head w elemencie title).
Elementem <jsp:doBody /> wskazuje miejsce gdzie zostanie wstawione ciało naszej bazowej strony, tzn. strony która będzie korzystać z szablonu.
Elementem jsp:directive.include możemy załączać inne pliku jsp do naszego szablonu. U nas w przykładzie załączamy jeden plik w którym docelowo będzie znajdować się menu aplikacji (dla celów testowych wypiszemy tylko napis Menu).Plik ten nazywa się menu.jsp i znajduje się w katalogu include.

Pliki jsp i jspx
Zacznijmy od stworzenia pliku menu.jsp z którego korzysta nasz szablon. W katalogu WebContent tworzymy folder include a w nim plik menu.jsp. Plik ten powinien zawierać kawałek kodu html i/lub jsp. U mnie wygląda to tak:
<div>
 Menu.
</div>

Kiedy mamy już stworzony pierwsze szablon, stwórzmy prosty plik który z niego skorzysta. W katalogu WebContent tworzymy plik main.jspx.
<layout:main xmlns:layout="urn:jsptagdir:/WEB-INF/tags/layout"
 xmlns:jsp="http://java.sun.com/JSP/Page">

 <jsp:attribute name="title">Tytuł strony z atrybutu</jsp:attribute>
 <jsp:directive.page contentType="text/html;charset=UTF-8" language="java" />

 Hello World !!!
 
</layout:main>
Na początku strony deklarujemy że używamy taglibu layout (nazwa naszego folder z szablonami). Nasz szablon zawierał jeden atrybut title, który musi zostać przekazany, w przeciwnym wypadku dostaniemy komunikat błędu. Atrybut możemy przekazać w dwojaki sposób:
  • poprzez dodanie nowego elementu jsp:attribute gdzie deklarujemy nazwę i wartość naszego atrybutu (tak jak w przykładzie)
  • poprzez dodanie do naszego główne elementu, atrybutu: nazwa_atrybutu_w_szablonie="wartość" (czyli: <layout:main title="Tytuł strony")

Po uruchomieniu aplikacji i wejściu na stronę main.jspx zobaczymy napis Hello World!!! pochodzący z naszej strony wraz z napisem Menu. (z pliku menu.jsp) oraz Layout góra, Layout dół.

czwartek, 10 czerwca 2010

JPA - Encje

W poniższym artykule zajmiemy się tworzeniem komponentów encyjnych. Encje reprezentują dane składowane w bazie danych w postaci zwykłej klasy javowej. Pola klasy są polami które odpowiadają polom w bazie danych, a metody get i set pozwalają nam pobierać i zmieniać dane w encji.

Tworzenie prostej encji
Aby obiekt stała się obiektem encyjnym potrzebujemy opatrzyć go adnotacją @Entity oraz zadeklarować klucz główny, czyli adnotacja @Id przy deklaracji pola lub przy metodzie get. Ja w moim kodzie wszystkie adnotacje związane z polami (ustalenie klucza głównego, relacje, dodatkowe zależności) będę umieszczał przy deklaracji pól, natomiast warto pamiętać że możemy używać ich również przy metodach get. Jeśli jednak zdecydujemy się umieścić adnotacje @Id przy polu, to konsekwentnie musimy trzymać się tego stylu do końca. W praktyce wygląda to tak.
package pl.mkorwel.exam.entities;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Osoba {

 @Id
 private long pole1;

 private String pole2;

 public long getPole1() {
  return this.pole1;
 }

 public String getPole2() {
  return this.pole2;
 }

 public void setPole1(final long pole1) {
  this.pole1 = pole1;
 }

 public void setPole2(final String pole2) {
  this.pole2 = pole2;
 }
}
Nasza encja nazywa się Osoba i zawiera dwa pole, pole1 jest kluczem głównym o typie numerycznym, a pole2 jest polem tekstowym. Obiekt encyjny zadeklarowany w ten sposób reprezentuje tabelę OSOBA z bazy danych z dwoma pola, POLE1 które jest kluczem głównym o typie numerycznych (BIGINT) oraz z polem POLE2 które jest zwykłym polem tekstowym VARCHAR(255).

Nazwy tabel i kolumn
Zadeklarowani encji bez wykorzystywania adnotacji @Table, @Column sprawi że nazwa klasy odpowiada nazwie tabeli, a nazwy pól odpowiadają nazwie kolumn w bazie danych. Jeśli jednak z jakiegoś powodu chcemy aby nasza encja miała inne nazewnictwo (lub innego powodu) niż tabela jaką reprezentuje jesteśmy zmuszeni dodać do klasy adnotację @Table, a do pól adnotację @Column.
package pl.mkorwel.exam.entities;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "OSOBA_TABLE")
public class Osoba {

 @Id
 @Column(name = "POLE_1", nullable = false)
 private long pole1;

 @Column(name = "POLE_2", length = 100)
 private String pole2;

 public long getPole1() {
  return this.pole1;
 }

 public String getPole2() {
  return this.pole2;
 }

 public void setPole1(final long pole1) {
  this.pole1 = pole1;
 }

 public void setPole2(final String pole2) {
  this.pole2 = pole2;
 }
}
Adnotacje @Table i @Column posiadają szereg atrybutów, dzięki którym możemy doprecyzować jak nasza tabela ma być odwzorowana w bazie danych.
@Table
  • name określa nazwę tabeli
  • catalog określa katalog w jakim znajduje się tabela
  • schema określa schemę w jakim znajduje się tabela
  • uniqueConstraints umożliwia definiowane kodu DDL

@Column
  • name nazwa kolumny
  • uniwue czy kolumna ma być unikalna
  • nullable czy wartość kolumny jest wymaga
  • insertable czy kolumna ma być brana pod uwagę podczas zapisu
  • updateable czy kolumna ma być brana pod uwagę podczas aktualizacji
  • uniqueConstraints umożliwia definiowane kodu DDL
  • length określa długość kolumny typu VARCHAR
  • table nazwa tabeli w jakiej znajduję się pole
  • precision dla kolumn o typie numerycznym, ustalamy ilość liczb po lewej stronie przecinka
  • scale dla kolumn o typie numerycznych, ustalamy ilość liczb po prawej stronie przecinka

Klczy główny
Ostatnim podstawowym zagadnieniem związanym z deklaracją encji jest klucz główny. Jak już wiemy każda encja musi mieć taki klucz, a pole które nim jest posiada adnotacje @Id. Klucz główny możemy oczywiście wpisywać ręcznie, ale istnieje też możliwość automatycznego generowania. Aby tego dokonać wystarczy umieścić adnotację @GeneratedValue.
package pl.mkorwel.exam.entities;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "OSOBA_TABLE")
public class Osoba {

 @Id
 @GeneratedValue
 @Column(name = "POLE_1", nullable = false)
 private long pole1;

 @Column(name = "POLE_2", length = 100)
 private String pole2;

 public long getPole1() {
  return this.pole1;
 }

 public String getPole2() {
  return this.pole2;
 }

 public void setPole1(final long pole1) {
  this.pole1 = pole1;
 }

 public void setPole2(final String pole2) {
  this.pole2 = pole2;
 }
}

Niektóre bazy danych oferują mechanizm sekwencyjnego generowania identyfikatorów, co powoduje ze musimy wskazać odpowiednią sekwencję, której będziemy używać do generowania. W takim celu musimy jeszcze umieścić adnotację @SequenceGenerator.
package pl.mkorwel.exam.entities;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "OSOBA_TABLE")
public class Osoba {

 @Id
 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "OSOBA_SEQ")
 @SequenceGenerator(name = "OSOBA_SEQ", sequenceName = "OSOBA_SEQ_W_BAZIE")
 @Column(name = "POLE_1", nullable = false)
 private long pole1;

 @Column(name = "POLE_2", length = 100)
 private String pole2;

 public long getPole1() {
  return this.pole1;
 }

 public String getPole2() {
  return this.pole2;
 }

 public void setPole1(final long pole1) {
  this.pole1 = pole1;
 }

 public void setPole2(final String pole2) {
  this.pole2 = pole2;
 }
}

W adnotacji @SequenceGenerator za pomocą atrybutu name nadajemy nazwę dla naszej sekwencji, a atrybutem sequenceName wskazujemy tabelę sekwencji w bazie danych. Następnie W adnotacji @GeneratedValue ustawiamy strategię generowania (atrybut strategy) na GenerationType.SEQUENCE i przy pomocy atrybutu generator wskazujemy która sekwencja ma zostać użyta.