Dostęp do danych z wykorzystaniem JPA
W żadnej aplikacji nie przyda się baza danych, jeśli nie będzie możliwości z niej skorzystania. Dawniej dostęp do bazy danych z poziomu Javy był zapewniany przez (JDBC)[https://docs.oracle.com/javase/tutorial/jdbc/basics/], który pozwalał na wykonywanie bezpośrednich zapytań na schemacie. Obecnie, m. in. dzięki rozwojowi biblioteki (Hibernate)[http://hibernate.org/] zadanie to można zrealizować na wyższym poziomie abstrakcji operując na modelach, które są zwykłymi obiektami POJO zarządzanymi przez kontener. To na podstawie tej biblioteki powstał standard JPA, pozwalający na uniezależnienie się od jakiejś konkretnej implementacji mechanizmu ORM.
Do stworzenia struktury bazy danych polecam Liquibase i opis przykładowego użycia: link
Najlepiej zacząć od dodaniu zależności do pom.xml:
<dependencies>
(...)
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
(...)
</dependencies>
Teraz tworzymy model:
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name = "person")
public class Person {
}
Jak widać, jedyna różnica pomiędzy encją, a zwykłym obiektem POJO, sprowadza się do 2 adnotacji:
- @Entity - oznacza klasę, jako encję
- @Table(name = “person”) - pozwala na sprecyzowanie tabeli, która będzie przechowywać rekordy danego typu (jeśli pominie się tę adnotację, JPA użyje domyślnej - odpowiadającej nazwie klasy)
Teraz pozostaje dodać odpowiednie pola (dla uproszczenia użyłem pól publicznych zamiast enkapsulacji z odpowiednimi metodami dostępowymi). Na początek klucz główny:
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
(...)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
public Integer id;
(...)
Użyte adnotacje:
- @Id - oznaczenie pola jako identyfikatora
- @GeneratedValue(strategy = GenerationType.IDENTITY) - wskazanie JPA, że wartość jest generowana, strategia IDENTITY zrzuca odpowiedzialność za poprawne wygenerowanie klucza na system zarządzania bazą danych
- @Column(name = “id”) - określenie kolumny w tabeli, która będzie przechowywać wartość pola Pozostałe pola:
import javax.persistence.Column;
import javax.validation.constraints.NotNull;
(...)
@Column(name = "first_name")
@NotNull
public String firstName;
@Column(name = "last_name")
@NotNull
public String lastName;
(...)
Użyte adnotacje:
- @Column - określenie kolumny w tabeli, która będzie przechowywać wartość pola
- @NotNull - oznaczenie ograniczenia not null
Do operacji na modelu wystarczy bezstanowy serwis EJB:
import javax.ejb.Stateless;
@Stateless
public class PersonBean {
}
Następnie można wstrzyknąć usługę EntityManager:
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
(...)
@PersistenceContext
private EntityManager entityManager;
(...)
Do pobrania wszystkich krotek można posłużyć się językiem JPQL (łudząco podobny do SQL, różniący się tym, że operuje na modelach, a nie na tabelach):
import java.util.List;
(...)
public List<Person> getAll() {
return entityManager
.createQuery("select p from Person p order by p.id", Person.class)
.getResultList();
}
(...)
Do pozostałych operacji wystarczą standardowe motody usługi EntityManager:
public Person getById(int id) {
return entityManager.find(Person.class, id);
}
public void create(Person person) {
entityManager.persist(person);
}
public void update(Person person) {
entityManager.merge(person);
}
public void delete(Person person) {
entityManager.remove(person);
}
Aby serwer aplikacyjny wiedział z jakiej bazy danych skorzystać, należy w katalogu src/main/resources/META-INF dodać plik persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="default" transaction-type="JTA">
<jta-data-source>java:/TestXADS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
</properties>
</persistence-unit>
</persistence>
W celu sprawdzenia poprawnego działania, można dodać serwis uruchamiany przy starcie serwera do którego wystarczy wstrzyknąć PersonBean:
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Inject;
import javax.transaction.Transactional;
@Singleton
@Startup
public class StartupBean {
private static final Logger logger = Logger.getLogger(StartupBean.class.getName());
@Inject
private PersonBean personBean;
@PostConstruct
@Transactional
public void createDefaultPersonAndCheckPeople() {
logger.info("People before startup:");
personBean.getAll().forEach(this::logPerson);
Person person = new Person();
person.firstName = "Jan";
person.lastName = "Kowalski";
personBean.create(person);
logger.info("People after startup:");
personBean.getAll().forEach(this::logPerson);
}
private void logPerson(Person person) {
logger.info(person.id + ". " + person.firstName + " " + person.lastName);
}
}
Po uruchomieniu aplikacji na serwerze, w logach powinna pojawić się informacja o stanie bazy danych przed i po aktualizacji:
People before startup:
People after startup:
1. Jan Kowalski
Przykładowy projekt: