Java i bazy danych

advertisement
Java i bazy danych
1. JDBC
●
podstawy,
●
transakcje.
2. Mapowanie relacyjno obiektowe.
●
Hibernate,
●
przykład.
1
JDBC - wprowadzenie
Java Database Connectivity (JDBC) to specyfkacja określająca zbiór
klas i interfejsów napisanych w Javie, które mogą być wykorzystane
przez programistów tworzących oprogramowanie korzystające z baz
danych. Implementacja JDBC jest dostarczany przez producentów baz
danych. Jedną z ważniejszych zalet takiego rozwiązania jest ukrycie
przed programistą kwestii technicznych dotyczących komunikacji
z bazą danych. Dzięki temu ten sam program napisany w Javie może
współpracować z różnymi systemami baz danych.
http://docs.oracle.com/javase/tutorial/jdbc/index.html
2
JDBC – połączenie
// zaladowanie klasy z driverem. opcjonalne stworzenie obiektu drivera
Class.forName("com.mysql.jdbc.Driver").newInstance();
// utworzenie obiektu java.sql.Connection
Connection con = DriverManager.getConnection(
"jdbc:mysql://localhost/test?user=monty&password=");
Nazwa klasy ze sterownikiem jak również postać argumentu metody
getConnection() jest uzależniona od używanego RDBMS'a i opisana
w jego dokumentacji (tutaj przykład dla MySQL).
Alternatywa: źródło danych DataSource i usługa JNDI:
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("jdbc/MojaDB");
Connection con = ds.getConnection("monty", "");
3
JDBC – polecenie
Statement stmt = con.createStatement();
Obiekt java.sql.Statement reprezentuje polecenie. Istnieją jego
rozszerzenia,
reprezentujące
polecenie
z
argumentami
(PreparedStatement) czy też wywołanie procedury bazodanowej
(CallableStatement).
4
JDBC – polecenie
Statement stmt = con.createStatement();
Obiekt java.sql.Statement reprezentuje polecenie. Istnieją jego
rozszerzenia,
reprezentujące
polecenie
z
argumentami
(PreparedStatement) czy też wywołanie procedury bazodanowej
(CallableStatement).
5
JDBC – zapytanie
ResultSet rs = stmt.executeQuery("SELECT * FROM tabela");
Obiekt java.sql.ResultSet umożliwia dostęp do wyników zapytania
(typu SELECT) wysłanego do bazy danych.
while (rs.next()) {
System.out.println(rs.getString("kolumna1"));
}
W ramach jednego polecenia (java.sql.Statement) można wykonać
wiele zapytań różnorodnego typu (SELECT, UPDATE, DELETE, ...).
Dostęp do statusu realizacji zapytań (błędy, ilość zmienionych
danych, wygenerowane klucze automatyczne) jest dostępny za
pomocą instancji java.sql.Statement.
6
JDBC – zwolnienie zasobów
rs.close()
stmt.close();
con.close();
Używane obiekty, jeśli nie będą już wykorzystywane, należy zwalniać
metodą close(). Metody dostępu do baz danych mogą zwracać
wyjątek (java.sql.SQLException), który musi zostać odpowiednio
obsłużony.
7
JDBC – modele komunikacji
klient - GUI
klient
aplikacja
aplikacja
aplikacja,
aplikacja,applet
applet
lub
lub
przeglądarka
przeglądarkaWWW
WWW
HTTP, RMI, CORBA
lub inne wywołanie
JDBC
JDBC
serwer
protokół
bazy danych
serwer
serweraplikacji
aplikacji
logika
biznesowa
JDBC
JDBC
baza
bazadanych
danych
serwer
protokół
bazy danych
baza
bazadanych
danych
8
JDBC – transakcje
Transakcje to zbiór operacji zgrupowanych w jednym lub wielu
obiektach Statement. Aby zakończyć transakcję należy wywołać
metodę commit() na rzecz obiektu Connection. Domyślnie metoda
commit() jest wywoływana po zakończeniu wykonywania zapytań
w ramach jednego obiektu Statement. Aby to zmienić należy użyć
metody setAutoCommit(false). Do anulowania zmian wprowadzonych
przez niezatwierdzoną transakcję służy metoda rollback().
9
JDBC – poziomy izolacji
Zwykle
w
systemy
baz
danych
realizują
jednocześnie
wiele
transakcji. Aby zapewnić kontrolę nad tym procesem wprowadzono
tzw. poziomy izolacji, poprzez które określa się zasady równoległej
realizacji kilku transakcji. JDBC przewiduje pięć poziomów izolacji:
●
●
TRANSACTION_NONE – brak transakcji.
TRANSACTION_READ_UNCOMMITTED – dopuszcza odczyt danych przed
wywołaniem metody commit().
●
TRANSACTION_READ_COMMITTED – inne transakcje nie mogą odczytywać
zmienionych wierszy przed wywołaniem metody commit() (dirty
reads).
10
JDBC – poziomy izolacji
●
TRANSACTION_REPEATABLE_READ – dodatkowo chroni przed sytuacją gdy
transakcja odczytuje wiersz, druga transakcja go zmienia a pierwsza
ponownie go odczytuje otrzymując inne dane (non-repetable reads).
●
TRANSACTION_SERIALIZABLE – dodatkowo chroni przed sytuacją, gdy
jedna transakcja odczytuje zbiór wierszy spełniający kryteria zawarte
w warunku WHERE, następnie druga transakcja wstawia wiersz
spełniający ten warunek, po czym pierwsza transakcja ponownie
odczytuje zbiór wierszy dostając nowy rekord (phantom-read).
Poziomy
izolacji
ustawia
się
metodą
setTransactionIsolation()
wywołaną na rzecz obiektu klasy Connection.
11
JDBC – etapy transakcji
Obiekt Savepoint (JDBC 3.0) umożliwia częściowe odwrócenie
(rollback) transakcji zamiast całkowitego. Do utworzenia tego
obiektu służy metoda setSavepoint().
Statement stmt = con.createStatement();
int rows = stmt.executeUpdate("INSERT INTO AUTHORS VALUES " +
"(LAST, FIRST, HOME) 'TOLSTOY', 'LEO', 'RUSSIA'");
Savepoint save1 = con.setSavepoint("SAVEPOINT_1");
int rows = stmt.executeUpdate("INSERT INTO AUTHORS VALUES " +
"(LAST, FIRST, HOME) 'MELVOY', 'HAROLD', 'FOOLAND'");
...
con.rollback(save1);
...
con.commit();
12
Mapowanie relacyjnoobiektowe
Mapowanie relacyjno-obiektowe (ORM) umożliwia zastąpienie
bezpośrednich operacji na bazie danych operacjami na obiektach
javy, reprezentującymi te dane. Operacje bazodanowe, związane z
np. z wyszukiwaniem czy aktualizacją tych obiektów odbywają się
(pół)automatycznie.
Najpopularniejszą
implementację
takiego
mapowania dla języka Java udostępnia biblioteka Hibernate (
http://www.hibernate.org).
13
ORM - przykład
contractors
con_id
con_name
con_address
orders
1:N
odr_id
odr_date
14
ORM struktura rzykładu
src
data
Contractor.java
ContractorManager.java
Contractor.hbm.xml
Order.java
OrderManager.java
Order.hbm.xml
util
HibernateUtil.java
Example.java
hibermate.cfg.xml
log4j.properties
15
ORM – Contractor.java
package data;
import java.util.HashSet;
import java.util.Set;
public class Contractor {
private int id;
private String name;
private String address;
private Set orders = new HashSet();
public Contractor(){
// pusty konstruktor
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
16
ORM – Contractor.java
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setAddress(String address) {
this.address = address;
}
public String getAddress() {
return this.address;
}
}
public void setOrders(Set orders) {
this.orders = orders;
}
public Set getOrders() {
return this.orders;
}
17
ORM – Contractor.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="data.Contractor" table="contractors">
<id name="id" column="con_id">
<generator class="native"/>
</id>
<property name="name" column="con_name"/>
<property name="address" column="con_address"/>
<set name="orders" inverse="true">
<key column="con_id"/>
<one-to-many class="data.Order"/>
</set>
</class>
</hibernate-mapping>
18
ORM – Order.java
...
public class Order {
private int id;
private Date date;
private Contractor contractor;
public Order(){ // pusty konstruktor }
public void setId(int id) {
this.id = id;
}
...
}
public int getId() {
return this.id;
}
19
ORM – Order.hbm.xml
...
<hibernate-mapping>
<class name="data.Order" table="orders">
<id name="id" column="odr_id">
<generator class="native"/>
</id>
<property name="date" type="date" column="odr_date"/>
<many-to-one name="contractor" column="con_id"
class="data.Contractor" />
</class>
</hibernate-mapping>
20
ORM –
ContractorManager.java
public class ContractorManager {
public void create(String sName, String sAddress) {
Session session = HibernateUtil.getSessionFactory().
getCurrentSession();
session.beginTransaction();
Contractor c = new Contractor();
c.setName(sName);
c.setAddress(sAddress);
session.save(c);
session.getTransaction().commit();
}
public Contractor load(int id) {
Session session = HibernateUtil.getSessionFactory().
getCurrentSession();
session.beginTransaction();
Contractor c = (Contractor) session.load(
Contractor.class, new Integer(id));
session.getTransaction().commit();
return c;
}
}
21
ORM – HibernateUtil.java
package util;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
// inicjalizacja na podstawie konfiguracji z pliku
// hibernate.cfg.xml
sessionFactory = new Configuration().configure().
buildSessionFactory();
} catch (Throwable ex) {
System.err.println("failure " + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
22
ORM - struktura rzykładu
src
data
Contractor.java
ContractorManager.java
Contractor.hbm.xml
Order.java
OrderManager.java
Order.hbm.xml
util
HibernateUtil.java
Example.java
hibermate.cfg.xml
log4j.properties
23
ORM - Example.java
public class Example {
public static void main(String[] args) {
ContractorManager cm = new ContractorManager();
if (args[0].equals("create")) {
Contractor c1, c2;
cm.create("Adam", "Lesna 11/3");
cm.create("Tomasz", "Zielona 123/65");
cm.create("Pawel", "Krotka 6");
c1 = cm.load(1);
c2 = cm.load(2);
OrderManager om = new OrderManager();
om.create(c1);
om.create(c2);
om.create(c1);
}
24
ORM - Example.java
}
}
else if (args[0].equals("print")) {
Order o;
Session session = HibernateUtil.getSessionFactory()
.GetCurrentSession();
session.beginTransaction();
List l = session.createQuery("from Contractor").list();
for (int i = 0; i < l.size(); i++) {
Contractor c = (Contractor) l.get(i);
System.out.println("Contractor:");
System.out.println(c.getId() + ", " + c.getName()) + ", "
+ c.getAddress());
for (Iterator it=c.getOrders().iterator(); it.hasNext();){
o = (Order) it.next();
System.out.println("Order:"+o.getId()+", "+o.getDate());
}
}
session.getTransaction().commit();
}
HibernateUtil.getSessionFactory().close();
25
ORM - Hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="connection.url">
jdbc:mysql://localhost/example
</property>
<property name="connection.username"></property>
<property name="connection.password"></property>
<property name="connection.pool_size">1</property>
26
ORM - Hibernate.cfg.xml
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="current_session_context_class">
thread
</property>
<property name="cache.provider_class">
org.hibernate.cache.NoCacheProvider
</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">create</property>
<mapping resource="data/Contractor.hbm.xml"/>
<mapping resource="data/Order.hbm.xml"/>
</session-factory>
</hibernate-configuration>
27
ORM - uruchomienie
Niezbędne biblioteki:
antlr.jar
asm.jar
asm-attrs.jars
cglib.jar
commons-collections.jar
commons-logging.jar
dom4j.jar
hibernate3.jar
jta.jar
log4j.jar
mysql-connector (http://dev.mysql.com/downloads/connector/j/)
Pierwsze
uruchomienie
programu
spowoduje
utworzenie
odpowiednich tabel:
java -cp [biblioteki] Example create
28
ORM - uruchomienie
Drugie uruchomienie programu: z pliku hibernate.cfg.xml usuwamy
element:
<property name="hbm2ddl.auto">create</property>
java -cp [biblioteki] Example print
Hibernate:
select
contractor0_.con_id
as
con1_0_,
contractor0_.con_name as con2_0_, contractor0_.con_address as
con3_0_ from contractors contractor0_
Contractor:
1, Adam, Lesna 11/3
Hibernate: select orders0_.con_id as con3_1_, orders0_.odr_id as
odr1_1_, orders0_.odr_id as odr1_1_0_, orders0_.odr_date as
odr2_1_0_, orders0_.con_id as con3_1_0_ from orders orders0_
where orders0_.con_id=?
Order: 3, 2006-04-11
Order: 1, 2006-04-11
Contractor:
...
29
Podsumowanie
Dostęp do baz danych jest możliwy poprzez interfejs JDBC. Aby z niego
skorzystać, należy pobrać sterownik (bibliotekę jar) dostarczony przez
producenta RDBMS implementującą ten inferfejs. Takie rozwiązanie
zapewnia możliwie dużą niezależność aplikacji od konkretego RDBMS.
Mapowanie relacyjno-obiektowe jest powszechnie wykorzystywane
w aplikacjach
klasy enterprise.
Przy
realizacji dużych
projektów,
znacznie upraszcza ono architekturę aplikacji, dodatkowo oddzielając ją
od RDBMS.
30
Download