Warning
This page is located in archive.

Implementace databázového klienta

Pro přístup do databáze lze v Javě použít

  • rozhraní JDBC zprostředkovávající komunikaci mezi klientem a databázovým serverem
  • Persistent API provádějící mapování z relační databáze do objektového návrhu
  • JPQL - technologie na pomezí výše zmíněných

JDBC

Objekt zajišťující připojení k databázi
import java.sql.*;
public class DatabaseConnection
{
  private static final String CONNECTION = //"jdbc:postgresql://SERVER:PORT/DATABASE";
  private static final String USERNAME = // USERNAME
  private static final String PASSWORD = // HESLO
  private Connection conn;
  public DatabaseConnection() throws ClassNotFoundException, SQLException
  {    /** Creates a new instance of DatabaseConnection */
      Class.forName("org.postgresql.Driver");
      this.conn = DriverManager.getConnection(CONNECTION, USERNAME, PASSWORD);
  }
  public Connection getConnection()
  {
      return this.conn;
  }
}
Objekt zajišťující operace nad databází
public class Storage {
  private static Storage storage = null;
  private Connection con;
  protected Storage() throws ClassNotFoundException, SQLException {
      DatabaseConnection dbconn = new DatabaseConnection();
      con = dbconn.getConnection();
  }
  public static Storage getInstance() throws ClassNotFoundException, SQLException {
      if (storage == null) {
          storage = new Storage();
      }
      return storage;
  }
  public List<Department> getAllDepartments() throws SQLException {
      List<Department> list = new ArrayList<Department>();
      Statement st = con.createStatement();
      ResultSet rs = st.executeQuery("SELECT * FROM department");
      while(rs.next()) {
          list.add(loadDepartment(rs));
      }
      return list;
  }
  protected Department loadDepartment(final ResultSet rs) throws SQLException {
      Department d = new Department();
      d.setDepartmentId(rs.getInt("department_id"));
      d.setDepartmentDescription(rs.getString("department_description"));
      return d;
  }
  public List<Employee> getAllEmployees() throws SQLException {
      List<Employee> list = new ArrayList<Employee>();
      Statement st = con.createStatement();
      ResultSet rs = st.executeQuery("SELECT * FROM employee");
      while(rs.next()) {
          list.add(loadEmployee(rs));
      }
      return list;
  }
  public List<Employee> getEmployeesByDepartment(Department dep) throws SQLException {
      List<Employee> list = new ArrayList<Employee>();
      PreparedStatement ps = con.prepareStatement("SELECT * FROM employee WHERE department_id=?");
      ps.setInt(1, dep.getDepartmentId());
      ResultSet rs = ps.executeQuery();
      while(rs.next()) {
          list.add(loadEmployee(rs));
      }
      return list;
  }
  protected Employee loadEmployee(final ResultSet rs) throws SQLException {
      Employee e = new Employee();
      e.setEmployeeId(rs.getInt("employee_id"));
      e.setBirthDate(rs.getDate("birth_date"));
      e.setFullName(rs.getString("full_name"));
      e.setDepartmentId(rs.getInt("department_id"));
      return e;
  }
  public void insertEmployee(Employee emp) throws SQLException {
      PreparedStatement ps = con.prepareStatement("INSERT INTO employee (employee_id,birth_date,full_name,department_id) VALUES (?,?,?,?)");
      ps.setInt(1, emp.getEmployeeId());
      ps.setDate(2, emp.getBirthDate());
      ps.setString(3, emp.getFullName());
      ps.setInt(4, emp.getDepartmentId());
      ps.executeUpdate();
  }
  public List<Salary> getAllSalaries() throws SQLException {
      List<Salary> list = new ArrayList<Salary>();
      Statement st = con.createStatement();
      ResultSet rs = st.executeQuery("SELECT * FROM salary");
      while(rs.next()) {
          list.add(loadSalary(rs));
      }
      return list;
  }
  public List<Salary> getSalariesByEmployee(Employee emp) throws SQLException {
      List<Salary> list = new ArrayList<Salary>();
      PreparedStatement ps = con.prepareStatement("SELECT * FROM salary WHERE employee_id=?");
      ps.setInt(1, emp.getEmployeeId());
      ResultSet rs = ps.executeQuery();
      while(rs.next()) {
          list.add(loadSalary(rs));
      }
      return list;
  }
  protected Salary loadSalary(final ResultSet rs) throws SQLException {
      Salary s = new Salary();
      s.setEmployeeId(rs.getInt("employee_id"));
      s.setPayDate(rs.getDate("pay_date"));
      s.setSalaryPaid(rs.getDouble("salary_paid"));
      return s;
  }
}

Java Persistence

Cílem tohoto tutoriálu je na jednoduchém příkladě ukázat základy práce s Java Persistence API v prostředí NetBeans verze 6.9. V první části tohoto tutoriálu se zaměříme na vytváření entit, manipulaci s entitami a vytvoření relací mezi entitami.

Založení projektu a vytvoření Persistence Unit pro připojení k databázi

  1. Založte si nový projekt typu Java Application v Netbeans
    • V menu File vyberte položku New project…
    • V dialogovém boxu vyberte v levém okně kategorii Java a v pravém okně položku Java Application
    • Klikněte na tlačítko Next
    • Název projektu změňte na JPADemo
    • Klikněte na tlačítko Finish
    • V properties projektu JPADemo zvolte JDK 6
  2. Nyní vytvořte připojení ke školní databázi
    • Ze záložky Projects se přepnšte do záložky Services a rozbalte uzel Databases
    • V uzlu Drivers zkontrolujte, že máte driver k PostgreSQL, jinak klepněte pravým tlačítkem myši na Drivers a následně pomocí New driver doplňte driver k PostgreSQL (driver link)
    • Nyní můžete přidat připojení k Vaší databázi, klepněte pravým tlačítkem myši na Databases, New Connection
    • Vyplňte údaje k Vaší školní databázi a potvrďte pomocí OK
    • Pozor, nyní musíte nastavit schéma PUBLIC a potvrďte pomocí OK
    • Klepněte pravým tlačítkem myši na nově vytvořené připojení a připojte se pro kontrolu ke své databázi.
  3. Dalším krokem je vlastní připojení k databázi, ta se připojuje pomocí Persistence Unit
    • V panelu se seznamem otevřených projektů vyberte projekt JPADemo.
    • V menu File vyberte položku New File…
    • V v dialogovém boxu vyberte v levém okně kategorii Persistencea v pravém okně položku Persistence Unit
    • Klikněte na tlačítko Next.
    • Jako název ponechte JPADemoPU, jako Persistence Library použijte EclipseLink(JPA 2.0), která bude pro naše účely dostačující.
    • Jako Database Connection vyberte již připravené připojení ke školní databázi
    • Jako Table Generation Strategy zvolte Create
    • Klikněte na tlačítko Finish.
    • V hlavním okně se otevře konfigurační soubor persistence.xml s nově vytvořenou JPADemoPU.
    • Kliknutím na tlačítko XML v levém horním rohu okna si můžete problédnout vygenerovaný XML soubor
    • Zkontrolujte a případně doplňte heslo
  4. Zkontrolujte, že mezi knihovnami projektu máte eclipselink.jar a eclipselink-javax.persistence.jar a přidejte knihovnu pro práci s databází Postgres

Vytváření entit

Základem objektově-relačního mapování je vytvoření dvojic entita - tabulka a atribut - sloupec v tabulce. V JPA jsou entity reprezentovány objekty typu POJO (plain old Java object), tj. objekty mají pouze atributy a funkce pro jejich získaní (getters) a pro jejich nastavení (setters). Předvedeme si to na příkladu s knihami.

@Entity
//@Table(name="book_table")
public class Book implements Serializable {
  @Id //@GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;
  @Column(nullable=false)
  private String title;
  @Column(length=2000)
  private String description;
  private Integer nbOfPages;
 
  public static Book createBook(Long id, String title, String description, Integer nbOfPages) {
        Book book = new Book();
        book.setId(id);
        book.setTitle(title);
        book.setDescription(description);
        book.setNbOfPages(nbOfPages);
        return book;
  }
 
  public String toString() {
        return String.valueOf(this.getId())+" ; "+this.getTitle()+" ; "+this.getDescription();
  }
 
  public Long getId() {
        return id;
  }
 
  public void setId(Long id) {
        this.id = id;
  }
 
  //dalsi getters a setters
  //V Netbeans je mozne getters a setters vygenerovat automaticky. 
  //Kliknete v okne editace pravym tlacitkem mysi a vyberte Refactor
  //a nasledne Encapsulate Fields. Nyni si muzete vybrat, 
  //ktere getters a setters si nechate vygenerovat.
}

Vysvětlivky:

  • @Entity je anotace, která říká, že objekt je entita. Nepovinně se uvádí i jméno tabulky pokud se liší od jména třídy @Table(name=“book_tab”).
  • @Id anotace pro označení primárního klíče s možností automatického generování hodnoty @GeneratedValue(strategy = GenerationType.AUTO)
  • @Column anotace pro upřesnění vlastností daného atributu
    • name - jméno v tabulce, pokud se liší od jména atributu
    • unique - vyžadována unikátní hodnota default false
    • nullable - nutno vyplnit default true
    • length - délka default 255

POZOR: Každou entitu je nutné přidat do seznamu entit v Persistence Unit. (sekce Include Entity Classes, nebo do tagu <class> v XML.

Manipulace s entitami

Nyní si ukážeme práci s entitami. V projektu JPADemo si vytvořte třídu Main typu Java Main Class, jejíž metodu main budeme editovat. Komunikace mezi aplikací a databází probíhá pomocí rozhraní EntityManager. V kódu je postupně ukázáno, jak vytvářet nové instance entit, jak je aktualizovat a mazat.

//Entity manager and transaction
EntityManagerFactory emf = Persistence.createEntityManagerFactory("JPADemoPU");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
 
//create new entity and persist it to the database
Book book = Book.createBook(123L, "JPA 2.0 - Mastering the Java Persistence API",
    "Kompletni pruvodce JPA 2.0",532);
tx.begin();
em.persist(book);
tx.commit();
 
//finding by ID
Book booka = em.find(Book.class, 123L);
System.out.println("Finding book> "+booka);
 
//removing an entity
Book bookrem = Book.createBook(124L, "Java", null, null);
tx.begin();
em.persist(bookrem);
tx.commit();
    Book bookf = em.find(Book.class, 124L);
    System.out.println("Remove book before> "+bookf);
tx.begin();
em.remove(bookrem);
tx.commit();
    Book bookg = em.find(Book.class, 124L);
    System.out.println("Remove book after> "+bookg);
 
//merging an entity outside transaction
Book bookmer = Book.createBook(125L, "Java", null, null);
tx.begin();
em.persist(bookmer);
tx.commit();
em.clear(); //demonstrate another work with database
    bookmer.setDescription("Zase nejaka Java");
    Book bookh = em.find(Book.class, 125L);
    System.out.println("Merging book before> "+bookh);
tx.begin();
em.merge(bookmer);
tx.commit();
    Book booki = em.find(Book.class, 125L);
    System.out.println("Merging book after> "+booki);
 
//update an entity inside transanction
Book booku = Book.createBook(126L, "Java 6", null, null);
tx.begin();
em.persist(booku);
booku.setDescription("No a jak jinak, zase Java.");
tx.commit();
    Book bookj = em.find(Book.class, 126L);
    System.out.println("Update book> "+bookj);

Úkol 1: Vytvořte novou entitu Author, která bude mít atributy id (typu @Id, automaticky generované), name (typ String délky 50) a surname (typ String délky 100, nutno vyplnit). Entity se budou mapovat do tabulky author_table. Nezapomeňte přidat třídu Author do Persistence Unit. Otestujte správnost vytvořením instance entity a kontrolou databázové tabulky.

Svázání entit relacemi

Nyní když máme dvě entity Book a Author je můžeme svázat relací typu 1:N.

V JPA můžeme použít jednosměrné (unidirectional) nebo obousměrné (bidirectional) relace. Vždy je nutné rozhodnout, která entita bude nositelem relace (odkaz na svázanou entitu je přímo atributem nosné entity). U obousměrné relace bude jedna entita nositelem relace a druhá entita bude inverzní (svázání je obousměrné, odkazy jsou přímo atributem obou entit).

Book a Author - 1:N jednosměrná - nositelem je Book:

public class Book
  @Id
  private Long id;  
  private String title;
  private String description;
  private Integer nbOfPages;
  @ManyToOne
  private Author author;
 
public class Author{
    @Id
    private Long id;
    private String name;
    private String surname;  
 

Book a Author - 1:N obousměrná - nositelem je Book:

public class Book
  @Id
  private Long id;  
  private String title;
  private String description;
  private Integer nbOfPages;
  @ManyToOne
  private Author authorRelation;
 
public class Author{
    @Id
    private Long id;
    private String name;
    private String surname;  
    @OneToMany(mappedBy = "authorRelation")
    private List<Book> books;

Úkol 2: Vyzkoušejte relaci mezi entitami. Vytvořte tři instance entity Book a dvě instance entity Author, správně je provázejte tak, aby první autor měl dvě knihy a druhý knihu jednu a vše uložte do databáze. Následně zjistěte autora jedné z knih. Zkontrolujte, jak se relace projevila v databázi. (Vyzkoušejte postupně jednosměrnou i obousměrnou relaci).

Odkazy

První část uzpůsobena pro potřeby předmětu Databázové systémy z tutoriálu JPA_v_NetBeans_6.0

Daleko podrobnější informace lze nalézt například v The Java EE 6 Tutorial

Další informace, viz např Entity @generated.

Java Persistence - JPQL

Cílem pokračování tutoriálu o JPA je ukázat možnosti dotazování (JPQL). Dotazování budeme opět zkoušet v databázi FoodMart, na které jsme již zkoušeli SQL příkazy. Syntax jazyka JPQL spolu s příklady dotazů v JPQL lze najít například v JPQL tutorial.

Stáhněte si Netbeans projekt cviceni10.zip, ve kterém je připraveno připojení k databázi a dvě entity Store a Employee. Do projektu doplňte knihovnu pro PostgreSQL, která je součástí zip souboru. Ve třídě Main je pak ukázka několika dotazů:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("DScviceniPU");
EntityManager em = emf.createEntityManager();
 
System.out.println("********************");
System.out.println("*  Dynamic Queries *");
System.out.println("********************");
 
TypedQuery queryS = em.createQuery("Select s from Store s", Store.class);
List<Store> listS = queryS.getResultList();
 
for (Iterator<Store> itS = listS.iterator(); itS.hasNext();) {
  Store store = itS.next();
  System.out.println(store);
}
 
System.out.println("*******************");
System.out.println("*  Named Queries  *");
System.out.println("*******************");
 
Query queryC = em.createNamedQuery(Employee.findByLastName);
queryC.setParameter("lastName", "Smith");
Long count = (Long)queryC.getSingleResult();
System.out.println(count);
 
System.out.println("*******************");
System.out.println("*  Native Queries *");
System.out.println("*******************");
 
Query queryN = em.createNativeQuery("SELECT first_name, last_name FROM Employee where last_name=?");
queryN.setParameter(1, "Smith");
List<Object[]> listN = queryN.getResultList();
for (Iterator<Object[]> itN = listN.iterator(); itN.hasNext();) {
  Object[] obj = itN.next();
  System.out.println(obj[0] + " " + obj[1]);
}

Úkoly (vždy použijte dynamické dotazy):

  • Vypište pouze zaměstnance s platem větším než zadaná konstanta.
  • Vypište pouze zaměstnance jejichž přijmení začíná na S a srovnejte je podle abecedy.
  • Zjistěte průměrné platy zaměstnanců - mužů podle profese. Profese vypište na obrazovku spolu s průměrným platem.
  • Vypište všechny zaměstnance, kteří pracují v obchodu Store 1.
courses/a4b33ds/cviceni-9.txt · Last modified: 2017/02/17 14:48 by komenant