======= 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 ==== {{courses:a4b33ds:ds-jdbc.zip|Ukázková konzolová implementace JDBC s DTO}} == 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 getAllDepartments() throws SQLException { List list = new ArrayList(); 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 getAllEmployees() throws SQLException { List list = new ArrayList(); Statement st = con.createStatement(); ResultSet rs = st.executeQuery("SELECT * FROM employee"); while(rs.next()) { list.add(loadEmployee(rs)); } return list; } public List getEmployeesByDepartment(Department dep) throws SQLException { List list = new ArrayList(); 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 getAllSalaries() throws SQLException { List list = new ArrayList(); Statement st = con.createStatement(); ResultSet rs = st.executeQuery("SELECT * FROM salary"); while(rs.next()) { list.add(loadSalary(rs)); } return list; } public List getSalariesByEmployee(Employee emp) throws SQLException { List list = new ArrayList(); 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 === - 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 - 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 ([[http://jdbc.postgresql.org/download.html|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. - 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 - 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 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 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 [[http://kore.fi.muni.cz:5080/wiki/index.php/JPA_v_NetBeans_6.0|JPA_v_NetBeans_6.0]] Daleko podrobnější informace lze nalézt například v [[http://download.oracle.com/javaee/6/tutorial/doc|The Java EE 6 Tutorial]] Další informace, viz např [[http://www.objectdb.com/java/jpa/entity/generated| 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 [[http://download.oracle.com/javaee/6/tutorial/doc/bnbtl.html|JPQL tutorial]]. Stáhněte si Netbeans projekt {{courses:a4b33ds:cviceni10.zip|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 listS = queryS.getResultList(); for (Iterator 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 listN = queryN.getResultList(); for (Iterator 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.