home_site

Lab03 - Jakarta Persistance API [ ver. ZTI.2025.03.28.003 ]

Zawartość strony

Zadania realizowane w ramach laboratorium

W ramach zajęć zostaną przedstawione zagadnienia związane z przetwarzaniem danych przechowywanych w bazach danych. Na początek omówiona zostanie usługa JNDI (Java Naming and Directory Interface) umożliwiająca mapowanie połączenia z bazą danych w katalogu usługi JNDI. Następnie zostaną przedstawione zagadnienia związane z tworzeniem pól połączeń (connection pools) do bazy danych. Centralnym zagadniem laboratorium będzie przedstawienie technologii JPA (Java Persistance API) umożliwiającej odwzorowanie obiektów Javy na odpowiednie rekordy w tablicach relacyjnej bazy danych. Omówione zostaną następujące zaganienia: interfejs programistyczny JPA ( zawierający zarządcę trwałości ), następnie encje odwzorowujące obiekty bazodanowe oraz tworzenie zapytań zgodnie z Java Persistence Query Language.

Skrypty opracowane w ramach zajęć.

  1. Odczyt danych z relacyjnej bazy danych (RBD) w skrypcie JSP z wykorzystaniem JNDI.
  2. Odczyt danych z RBD w skrypcie JSF z wykorzystaniem JPA (zarządzanie na poziomie kontenera).
  3. Prosta aplikacja CRUD w technologii JSF zrealizowana z wykorzystaniem JPA (zarządzanie przez kontener).
  4. Realizacja aplikacji CRUD w technologii JSF i JPA zarządzane w ramach aplikacji.
  1. Usługa JNDI i skrypt JSP

    1. W ramach technologii JEE dostępna jest technologia JNDI, której celem jest dostarczenie jednolitego interfejsu w ramach rozprosznej aplikacji do innych komponentów i zasobów tj. bazy danych, sesje wiadomości e-mail, obiekty JMS ( Java Message Service) czy serwis LDAP [1,2,3]. W ramach interfejsu JNDI każdy obiekt zasobu jest identyfikowany przez unikalna nazwę, która jest czytelna dla programisty, dostęp poprzez JDBC do bazy danych określny jest np. przez nazwę np. jdbc/bazadb. Obiekt zasobu i jego nazwa JNDI są powiązane przez usługi katalogowe. W ramach serwera aplikacyjnego za obsługę JNDI odpowiada odpowieni serwis natomiast w ramach kontenera Web funkcjonalność JNDI uzyskujemy poprzez odpowiedni wpis w plikach konfigurujących serwera. Architektura interfejsu JNDI przedstawiona została na rys.1 ( rysunek z dokumentacji Oracle ). W naszej aplikacji wykorzystamy serwer aplikacyjny Apache TomEE a odpowiednie wpisy zostaną umieszczone w plikach konfiguracyjnych context.xml, resources.xml i web.xml.
      Lab03_JNDI_Architecture
      Rys.1. Architektura interfejsu JNDI (Oracle).
    2. W ramach laboratorium opracujemy kilka przykładów połączenia z bazą danych w ramach aplikacji tworzonych w ramach środowiska JEE w kontenerze WEB Apache TomEE:
      • konfiguracja usługi JNDI na serwerze aplikacyjnym Apache TomEE,
      • testowe połączenie z bazą danych z wykorzystaniem skrpyptu JSP i usługi JNDI,
      • skrypt JSF łączący się z bazą danych poprzez JDBC i JNDI,
      • skrypt JSF łączący się z bazą danych poprzez JDBC, JNDI i interfejs JPA,
      • prosta aplikacja CRUD z wykorzystaniem JSF i JPA.
    3. W środowisku IntelliJ tworzymy nowy projekt ZTI_Lab03 typu Jakarta Enterprise ( rys.2 i rys.3 ).
      Lab03_proj_conf_1
      Rys.2. Konfiguracja projektu w ramach IDE IntelliJ.
      Lab03_proj_conf_2
      Rys.3. Konfiguracja projektu w ramach IDE IntelliJ.
    4. Do realizacji zadań wykorzystujących JNDI i skrypt JSP przygotujemy odpowiednie środowisko uruchomieniowe. Na początek tworzymy plik context.xml (tworzymy plik typu xml) w katalogu webapp/META-INF i umieszczamy w nim poniższą zawartość.

      Plik konfiguracyjny context.xml - ( [listing dokumentu] [link do dokumentu]

         <?xml version="1.0" encoding="UTF-8"?>
      <Context>
          <Resource name="jdbc/postgres"
                    auth="Container"
                    type="javax.sql.DataSource"
                    driverClassName="org.postgresql.Driver"
                    url=" -- jdbc url --"
                    username=" -- user -- "
                    password=" -- password -- "
                    maxTotal="1"
                    maxIdle="1"
                    maxWaitMillis="-1"/>
      </Context>
      gdzie podmieniamy odpowiednio:
      • [[dburl]] - jdbc:postgresql://host:5432/dbname naszej bazy danych na serwerze Pascal (jak na poprzednich laboratoriach),
      • [[dbuser]] - użytkownika bazy danych user,
      • [[dbpassword]] - hasło do naszej bazy danych.
      Do poprawnej funkcjonalności naszego projektu wymagane jest jeszcze umieszczenie biblioteki obsługującej bazę PostgreSQL w projekcie postgresql-42.7.5.jar, które dołączamy do pliku konfiguracyjnego menadżera Maven.
              <dependency>
                  <groupId>org.postgresql</groupId>
                  <artifactId>postgresql</artifactId>
                  <version>42.7.5</version>
              </dependency>
      

      Dodatkowo na potrzeby realizacji pliku w JSP z funkcjonalnością JSTL dodamy do pliku pom.xml poniższe zależności.

              <dependency>
                  <groupId>jakarta.servlet.jsp.jstl</groupId>
                  <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
                  <version>3.0.0</version>
              </dependency>
      		<dependency>
                  <groupId>org.glassfish.web</groupId>
                  <artifactId>jakarta.servlet.jsp.jstl</artifactId>
                  <version>3.0.1</version>
              </dependency>
      
    5. Na początek przetestujemy nasze połączenie z relacyjną bazą danych z wykorzystanie skryptu JSP i JSTL. Tworzymy skrypt JSP TestJNDI.jsp z poniższą zawartością.

      Skrypt TestJNDI.jsp - [listing dokumentu] [link do dokumentu]

      <%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %>
      <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
      
      <sql:query var="rs" dataSource="jdbc/postgres">
      SELECT fname, lname FROM public.person
      </sql:query>
      
      <html>
        <head>
          <title>JSP, JNDI i JDBC</title>
        </head>
        <body>
      
        <h2>Polaczenie z RBD - JSP & JSTL & JNDI & JDBC</h2>
      
      <c:forEach var="row" items="${rs.rows}">
          ${row.fname}, ${row.lname}<br/>
      </c:forEach>
      
        </body>
      </html>
    6. Sprawdzamy poprawność działania naszego skryptu http://localhost:8080/zti_lab03 ... /TestJNDI.jsp. Poprawnie przygotowane środowisko, połączenie z bazą danych oraz skrypt powinny umożliwić otrzymanie wyniku przedstawionego na rys.4.
      Lab03_jsp_JNDI
      Rys.4. Test interfejsu JNDI do bazy dany w skrypcie JSP.
  2. Framework JSF i technologia JPA

    1. W ramach tego zadania opracujemy prosty serwis w technologii JSF odczytujący dane z bazy danych na serwerze Pascal z wykorzystaniem interfesju JPA.
    2. Na początek przygotujemy środowisko uruchomieniowe dla skryptów JSF. Wymagany jest utworzenie skryptu faces-config.xml w katalogu WEB-INF oraz modyfikacja skryptu web.xml.
      Tworzymy plik faces-config.xml (można skorzystać z kreatora w ramach narzędzia IDEA).
      <?xml version="1.0" encoding="UTF-8"?>
      
      <faces-config 
           version="4.0" xmlns="https://jakarta.ee/xml/ns/jakartaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
           https://jakarta.ee/xml/ns/jakartaee/web-facesconfig_4_0.xsd" >
      
      
      	<application>
      		<resource-bundle>
      			<base-name>data.messages</base-name>
      			<var>msg</var>
      		</resource-bundle>
      		<resource-bundle>
      			<base-name>data.mess-errors</base-name>
      			<var>errors</var>
      		</resource-bundle>
      	</application>
      
      </faces-config>
      
      Poniżej wymagana modyfikacja w pliku web.xml.
        <servlet>
          <servlet-name>Faces Servlet</servlet-name>
          <servlet-class>jakarta.faces.webapp.FacesServlet</servlet-class>
          <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
          <servlet-name>Faces Servlet</servlet-name>
          <url-pattern>*.xhtml</url-pattern>
        </servlet-mapping>
      
    3. W kolejnym punkcie przygotowujemy plik konfiguracyjny persistence.xml zawierający informację na temat źródła danych i encji wykorzystywanej w projekcie. W ramach tego zadania zostanie opracowana jedna encja person odwzorowująca tabelę PERSON w bazie danych.
    4. Plik persistence.xml zawiera informację o bazie danych, informację o implemantacji JPA w ramach technologii JTA. Plik umieszczamy w ramach IDE w katalogu resources/META-INF (standardowo umieszczany również przez IDE). Zawartość pliku przedstawiona została poniżej. Dostęp do bazy danych zrealizowany został poprzez interfejs JNDI ("jdbc/postgres"), którego parametry zostały ustawione w pliku resources.xml.

      Plik konfiguracyjny persistence.xml - [listing dokumentu] [link do dokumentu]

        <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
      <!--suppress ALL, JpaConfigDomFacetInspection -->
      <persistence xmlns="https://jakarta.ee/xml/ns/persistence"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence 
      			    https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
                   version="3.0">
          <persistence-unit name="PU_Postgresql" transaction-type="JTA">
              <jta-data-source>java:comp/jdbc/postgres</jta-data-source>
              <class>zti.model.Person</class>
              <properties>
                  <property name="eclipselink.target-database"
                            value="org.eclipse.persistence.platform.database.PostgreSQLPlatform" />
                  <property name="eclipselink.logging.level" value="FINER" />
                  <property name="eclipselink.logging.logger" value="ServerLogger" />
              </properties>
          </persistence-unit>
      </persistence>  

      Plik konfiguracyjny resources.xml - [listing dokumentu] [link do dokumentu]

        <?xml version="1.0" encoding="UTF-8"?>
      <resources>
          <Resource id="jdbc/postgres" type="javax.sql.DataSource">
              JdbcDriver = org.postgresql.Driver
              JdbcUrl   = -- jdbc-url ---
              UserName  = -- user --
              Password  = -- password --
              JtaManaged = false
              initialSize = 2
              maxActive = 2
              maxIdle = 1
              minIdle = 1
          </Resource>
      </resources>  
    5. Zawartość pliku resources.xml jest dość podobna do zawartości pliku context.xml. Plik resources.xml umieszczamy w katalogu webapp/WEB-INF.
    6. W kolejnym punkcie przygotujemy encję dla tabeli bazodanowej PERSON. Klasę tworzymy w pakiecie model.

      Klasa Person.java - ( [listing dokumentu] [link do dokumentu]

        package zti.model;
      
      import java.io.Serializable;
      import jakarta.persistence.*;
      
      
      /**
       * The persistent class for the person database table.
       *
       */
      @Entity (name="person")
      @Table (name="person", schema="public")
      
      public class Person implements Serializable {
          private static final long serialVersionUID = 1L;
          private Integer id;
          private String city;
          private String email;
          private String fname;
          private String lname;
          private String tel;
      
          public Person() {
          }
      
      
          @Id
          //@SequenceGenerator(name="PERSON_ID_GENERATOR" )
          //@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="PERSON_ID_GENERATOR")
          @GeneratedValue(strategy=GenerationType.IDENTITY)
          public Integer getId() {
              return this.id;
          }
      
          public void setId(Integer id) {
              this.id = id;
          }
      
          public String getCity() {
              return this.city;
          }
      
          public void setCity(String city) {
              this.city = city;
          }
      
      
          public String getEmail() {
              return this.email;
          }
      
          public void setEmail(String email) {
              this.email = email;
          }
      
      
          public String getFname() {
              return this.fname;
          }
      
          public void setFname(String fname) {
              this.fname = fname;
          }
      
      
          public String getLname() {
              return this.lname;
          }
      
          public void setLname(String lname) {
              this.lname = lname;
          }
      
      
          public String getTel() {
              return this.tel;
          }
      
          public void setTel(String tel) {
              this.tel = tel;
          }
      
      
      }  
    7. Obsługa funkcjonalności JPA i połaczenie z bazą danych zostanie zrealizowana w klasie PersonRepository.java. Klasa tworzy manadżer trwałości na podstawie pliku persistence.xml.

      Klasa PersonRepository.java - [listing dokumentu] [link do dokumentu]

        package zti.model;
      
      import jakarta.ejb.Stateless;
      import jakarta.persistence.*;
      
      import java.io.Serializable;
      import java.util.List;
      
      @Stateless
      public class PersonRepository implements Serializable {
      
          @PersistenceContext
          EntityManager entityManager ;
      
          public List<Person> getAllPersons() {
              return entityManager.createQuery("select p from person p", Person.class)
                      .getResultList();
          }
      
      
      }  
    8. Kolejną klasą jest ServJSF obsługująca żądania do warstwy trwałości wysyłane z skryptu JSF.

      Klasa ServJSF.java - [listing dokumentu] [link do dokumentu]

        package zti.lab3;
      
      import java.io.Serializable;
      import java.util.List;
      
      import jakarta.enterprise.context.SessionScoped;
      import jakarta.inject.Inject;
      import jakarta.inject.Named;
      
      import zti.model.Person;
      import zti.model.PersonRepository;
      
      @Named ("servJPABean")
      @SessionScoped
      
      public class ServJSF implements Serializable {
      
          private Person editPerson;
          private Person viewPerson;
          private Person addPerson = new Person();
      
          @Inject
          PersonRepository personRepository ;
      
          public List<Person> getPeople() {
              return personRepository.getAllPersons();
          }
      
      
      
      }
      
        
    9. Formularz JSF wyświetlający dane z bazy danych - Postgresql - z wykorzystaniem JPA.

      Skrypt viewJPA.xhtml - [listing dokumentu] [link do dokumentu]

        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core">
      <head>
          <title>Test - view JPA</title>
      </head>
      <body>
      <div id="content">
          <h1>#{msg.titleFormJPA}</h1>
          <h2>#{msg.subtitleFormJPA}</h2>
          <h3>#{msg.titleFormJPAPostgresql}</h3>
          <h:form>
              <h:dataTable value="#{servJPABean.people}" var="record1" border="1">
                  <h:column>
                      <!-- kolumna zawiera nazwiska osob -->
                      <f:facet name="header">
                          <h:outputText value="#{msg.Lastname}" />
                      </f:facet>
                      <h:outputText value="#{record1.lname}" />
                  </h:column>
                  <h:column>
                      <!-- kolumna zawiera imiona osob -->
                      <f:facet name="header">
                          <h:outputText value="#{msg.Firstname}" />
                      </f:facet>
                      <h:outputText value="#{record1.fname}" />
                  </h:column>
                  <h:column>
                      <!-- kolumna zawiera e-mail osob -->
                      <f:facet name="header">
                          <h:outputText value="#{msg.Email}" />
                      </f:facet>
                      <h:outputText value="#{record1.email}" />
                  </h:column>
      
              </h:dataTable>
          </h:form>
      </div>
      </body>
      </html>  
    10. Uzupełniamy zawartość pliku messages.properties (resources/data) i sprawdzamy poprawność działania skryptów (rys.5).
      titleFormJPA=Prezentacja interfejsu JPA
      subtitleFormJPA=Parametry polaczenia w usludze JNDI
      titleFormJPAPostgresql=Odczyt danych z bazy danych PostgreSQL
      
      Firstname=Imie
      Lastname=Nazwisko
      City=Miasto
      Email=E-mail
      Tel=Telefon
      
      Lab03_JPA_view
      Rys.5. Rekordy w bazie danych PostgreSQL (interfejs JPA).
  3. JPA - aplikacja CRUD

    1. W ramach tego zadania zapoznamy się z przetwarzaniem zapytań do bazy danych poprzez intefejs JPA wykorzytując metody menadżera trwałości tj. find(), merge(), flush(), remove() czy persist(). Zapytania do bazy danych bedą realizowane w ramach transakcji nadzorowanych przez serwer aplikacyjny (JTA). Przetwarzanie zapytań przez JPA będzie realizowane w ramach wcześniej utworzonej klasy PersonRepository, którą uzupełnimy o dodatkowe funkcjonalności.
      • dodanie rekordu do bazy danych - metoda persist(),
      • modyfikacja rekordu w bazie danych - merge(),
      • usunięcie rekordu z bazy danych - remove(),
      • pobranie rekordu z bazy danych - find(),
      • synchronizacja pomiędzy bazą danych a kontekstem trwałości - flush(),
      • przykładowe zapytanie nazwane JPA QL - createNamedQuery("nazwa").getResultList().
    2. Poniżej zawarta jest implementacja przedstawionych powyżej funkcjonalności w ramach klasy PersonRepository.java (dodatkowe metody).
          public void savePerson( Person entity) {
              System.out.println("[ConnJPA] Save entity - " + entity.getLname() );
              entityManager.persist(entity);
              entityManager.flush();
          }
      
          public void updatePerson(Person entity) {
              entityManager.merge(entity);
          }
      
          public void deletePerson(Integer id ) {
              Person p = entityManager.find(Person.class, id);
              entityManager.remove(p);
              entityManager.flush();
          }
      
          public Person findPerson(Person entity) {
              return entityManager.find(Person.class, entity);
          }
      
    3. Kolejną klasą, którą uzupełnimy jest klasa zarządzana ServJSF.java obsłująca JSF. Poniżej zawartość klasy po dodaniu dodatkowych metod.

      Klasa ServJSF.java - [listing dokumentu] [link do dokumentu]

      package zti.lab3;
      
      import java.io.Serializable;
      import java.util.List;
      
      import jakarta.enterprise.context.SessionScoped;
      import jakarta.inject.Inject;
      import jakarta.inject.Named;
      
      import zti.model.Person;
      import zti.model.PersonRepository;
      
      @Named ("servJPABean")
      @SessionScoped
      
      public class ServJSF implements Serializable {
      
          private Person editPerson;
          private Person viewPerson;
          private Person addPerson = new Person();
      
          @Inject
          PersonRepository personRepository ;
      
          public List<Person> getPeople() {
              return personRepository.getAllPersons();
          }
      
          public String delPerson(Integer id) {
              personRepository.deletePerson(id);
              return "allRecPost" ;
          }
      
          public Person getEditPerson() {
              return editPerson;
          }
      
          public Person getAddPerson() {
              return addPerson;
          }
      
          public Person getViewPerson() {
              return viewPerson;
          }
      
          public String selPerson(Person entity) {
              viewPerson = copy(entity);
              return "viewRecPost" ;
          }
      
          public String updPerson(Person entity) {
              editPerson = copy(entity);
              return "updRecPost" ;
          }
      
          public String updatePerson() {
              personRepository.updatePerson(editPerson);
              editPerson = new Person();
              return "allRecPost";
          }
      
          public String savePerson() {
              personRepository.savePerson(addPerson);
              addPerson = new Person();
              return "allRecPost";
          }
      
      
      
          private Person copy(Person entity) {
              Person p = new Person();
              p.setId(entity.getId());
              p.setFname(entity.getFname());
              p.setLname(entity.getLname());
              p.setCity(entity.getCity());
              p.setEmail(entity.getEmail());
              p.setTel(entity.getTel());
              return p ;
          }
      
      }
      
      
    4. Do zamknięcia zadania pozostała jeszcze realizacja serwisu WWW. Serwis zostanie zrealizowany w oparciu o doświadczenia z zadania realizowanego na laboratorium z JSF. Na początek utworzymy odpowiednie szablony a następnie kolejne strony obsługujące wymaganą funkcjonalność serwisu.
    5. Szablon główny aplikacji masterTemplate.xhtml umieszczony w katalogu templates.

      Skrypt masterTemplate.xhtml - [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core">
      <h:head>
          <title>#{msg.siteTitleCRUD}</title>
          <h:outputStylesheet library="css" name="style.css" />
      </h:head>
      
      <h:body id="main">
          <ui:debug hotkey="x" rendered="#{initParam['javax.faces.FACELETS_DEVELOPMENT']}"/>
      
          <div id="header">
              <ui:insert name="header">
                  <ui:include src="/section/main/header.xhtml"/>
              </ui:insert>
          </div>
      
          <div id="sidebar">
              <ui:insert name="sidebar">
                  <ui:include src="/section/main/sidebarLeft.xhtml"/>
              </ui:insert>
          </div>
      
          <div id="content">
              <ui:insert name="content">
              </ui:insert>
          </div>
      
          <div id="footer">
              <ui:insert name="footer">
                  <ui:include src="/section/main/footer.xhtml"/>
              </ui:insert>
          </div>
      
      </h:body>
      
      </html>
      
    6. Szablon nagłówka header.xhtml umieszczony w katalogu section/main.

      Skrypt header.xhtml - [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core">
      <head>
          <title>IGNORED</title>
      </head>
      
      <body>
      <ui:composition>
          <!-- div class="header"-->
          <h1>#{msg.headCRUDappl}</h1>
          <!-- /div-->
      </ui:composition>
      </body>
      </html>
      
    7. Szablon stopki footer.xhtml umieszczony w katalogu section/main.

      Skrypt footer.xhtml - [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core">
      <head>
         <title>IGNORED</title>
      </head>
      <body>
      <ui:composition>
         #{msg.footerCRUDappl}
         <!--div class="footer"></div-->
      </ui:composition>
      </body>
      </html>
      
    8. Szablon menu sidebarLeft.xhtml umieszczony w katalogu section/main.

      Skrypt sidebarLeft.xhtml - [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core">
      <head>
         <title>IGNORED</title>
      </head>
      <body>
      <ui:composition>
         <!--div class="sidebar"-->
         <h:form>
      
            <h:link value="#{msg.linkAllRecPostgresqlSidebar}"   id="all_Postgresql"  outcome="allRecPost" /> <br/>
            <h:link value="#{msg.linkAddRecPostgresqlSidebar}" id="add_Postgresql"  outcome="addRecPost" />	<br/>
      
         </h:form>
         <!--/div-->
      </ui:composition>
      </body>
      </html>
      
    9. Skrypt główny zadania viewCRUD.xhtml umieszczony w katalogu głównym.

      Skrypt viewCRUD.xhtml - [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core">
      <head>
         <title>IGNORED</title>
      </head>
      <body>
      
      <ui:composition template="/templates/masterTemplate.xhtml" >
         <ui:define name="content">
            #{msg.welcomePageCRUD}
         </ui:define>
      </ui:composition>
      
      </body>
      </html>
      
      
    10. Skrypt obsługujący pobranie wszystkich rekordów z bazy danych allRecPost.xhtml umieszczony w katalogu głównym.

      Skrypt allRecPost.xhtml - [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core">
      <head>
          <title>IGNORED</title>
      </head>
      <body>
      
      <ui:composition template="/templates/masterTemplate.xhtml" >
          <ui:define name="content">
              <div id="content">
                  <h3>#{msg.titleFormJPAPostgresql}</h3>
                  <h:form>
                      <h:dataTable value="#{servJPABean.people}" var="record1"
                                   border="1">
                          <h:column>
                              <!-- kolumna zawiera nazwiska osob -->
                              <f:facet name="header">
                                  <h:outputText value="#{msg.Lastname}" />
                              </f:facet>
                              <h:outputText value="#{record1.lname}" />
                          </h:column>
                          <h:column>
                              <!-- kolumna zawiera imiona osob -->
                              <f:facet name="header">
                                  <h:outputText value="#{msg.Firstname}"/>
                              </f:facet>
                              <h:outputText value="#{record1.fname}" />
                          </h:column>
                          <h:column>
                              <!-- kolumna zawiera e-mail osob -->
                              <f:facet name="header">
                                  <h:outputText value="#{msg.Email}" />
                              </f:facet>
                              <h:outputText value="#{record1.email}" />
                          </h:column>
      
                          <h:column>
                              <f:facet name="header">
                                  <h:outputText value="#{msg.actionFormList}" />
                              </f:facet>
                              [<h:commandLink id="View" action="#{servJPABean.selPerson(record1)}" >
                              <h:outputText value="#{msg.viewFormCRUD}" />
                          </h:commandLink>]
                              [<h:commandLink id="Edit" action="#{servJPABean.updPerson(record1)}" >
                              <h:outputText value="#{msg.editFormCRUD}" />
                          </h:commandLink>]
                              [<h:commandLink id="Delete" action="#{servJPABean.delPerson(record1.id)}" >
                              <h:outputText value="#{msg.delFormCRUD}" />
                          </h:commandLink>]
                          </h:column>
      
                      </h:dataTable>
                  </h:form>
      
              </div>
          </ui:define>
      </ui:composition>
      
      </body>
      </html>
      
      
      
      
      
      
    11. Skrypt obsługujący dodanie rekordu do bazy danych addRecPost.xhtml umieszczony w katalogu głównym.

      Skrypt addRecPost.xhtml - [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core">
      <head>
          <title>IGNORED</title>
      </head>
      
      <body>
      <ui:composition template="/templates/masterTemplate.xhtml">
      
          <ui:define name="content">
              <div id="content">
                  <h3>#{msg.titleFormAdd}</h3>
      
                  <h:form>
                      <h:panelGrid columns="3" frame="hsides" cellspacing="3">
                          <h:outputLabel for="fname" value="#{msg.Firstname}" />
                          <h:inputText id="fname"
                                       value="#{servJPABean.addPerson.fname}" required="true"
                                       requiredMessage="#{errors.AddFirstname}" />
                          <h:message for="fname" />
                          <h:outputLabel for="lname" value="#{msg.Lastname}" />
                          <h:inputText id="lname"
                                       value="#{servJPABean.addPerson.lname}" required="true"
                                       requiredMessage="#{errors.AddLastname}" />
                          <h:message for="lname" />
                          <h:outputLabel for="city" value="#{msg.City}" />
                          <h:inputText id="city"
                                       value="#{servJPABean.addPerson.city}" />
                          <h:message for="city" />
                          <h:outputLabel for="email" value="#{msg.Email}" />
                          <h:inputText id="email"
                                       value="#{servJPABean.addPerson.email}" required="true"
                                       requiredMessage="#{errors.AddEmail}">
                          </h:inputText>
                          <h:message for="email" />
                          <h:outputLabel for="tel" value="#{msg.Tel}" />
                          <h:inputText id="tel"
                                       value="#{servJPABean.addPerson.tel}" />
                          <h:message for="tel" />
      
                      </h:panelGrid>
                      <h:commandButton value="#{msg.saveLinkFormAdd}"
                                       action="#{servJPABean.savePerson}" id="save" />
      
                  </h:form>
      
              </div>
      
          </ui:define>
      
      </ui:composition>
      
      
      </body>
      
      </html>
      
    12. Skrypt obsługujący poprawę rekordu z bazy danych updRecPost.xhtml umieszczony w katalogu głównym.

      Skrypt updRecPost.xhtml - [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core">
      <head>
          <title>IGNORED</title>
      </head>
      
      <body>
      <ui:composition template="/templates/masterTemplate.xhtml">
      
          <ui:define name="content">
      
              <div id="content">
                  <h3>#{msg.titleFormUpdate}</h3>
                  <h:form>
                      <h:panelGrid columns="3" frame="hsides" cellspacing="3">
                          <h:outputLabel for="fname" value="#{msg.Firstname}" />
                          <h:inputText id="fname"
                                       value="#{servJPABean.editPerson.fname}" />
                          <h:outputText value="" />
                          <h:outputLabel for="lname" value="#{msg.Lastname}" />
                          <h:inputText id="lname"
                                       value="#{servJPABean.editPerson.lname}" />
                          <h:outputText value="" />
                          <h:outputLabel for="city" value="#{msg.City}" />
                          <h:inputText id="city"
                                       value="#{servJPABean.editPerson.city}" />
                          <h:outputText value="" />
                          <h:outputLabel for="email" value="#{msg.Email}" />
                          <h:inputText id="email"
                                       value="#{servJPABean.editPerson.email}" />
                          <h:outputText value="" />
                          <h:outputLabel for="tel" value="#{msg.Tel}" />
                          <h:inputText id="tel"
                                       value="#{servJPABean.editPerson.tel}" />
                          <h:outputText value="" />
                      </h:panelGrid>
                      <h:commandButton value="#{msg.saveLinkFormUpdate}"
                                       action="#{servJPABean.updatePerson}" id="update" />
                  </h:form>
              </div>
          </ui:define>
      </ui:composition>
      </body>
      </html>
      
    13. Na koniec skrypt obsługujący wyświetlenie rekordu z bazy danych viewRecPost.xhtml umieszczony w katalogu głównym.

      Skrypt viewRecPost.xhtml - [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core">
      <head>
          <title>IGNORED</title>
      </head>
      
      <body>
      <ui:composition template="/templates/masterTemplate.xhtml">
          <ui:define name="content">
              <div id="content">
                  <h3>#{msg.titleFormView}</h3>
                  <h:form>
                      <h:panelGrid columns="2" frame="hsides" cellspacing="2">
                          <h:outputLabel for="fname" value="#{msg.Firstname}" />
                          <h:outputText id="fname"
                                        value="#{servJPABean.viewPerson.fname}" />
                          <h:outputLabel for="lname" value="#{msg.Lastname}" />
                          <h:outputText id="lname"
                                        value="#{servJPABean.viewPerson.lname}" />
                          <h:outputLabel for="city" value="#{msg.City}" />
                          <h:outputText id="city"
                                        value="#{servJPABean.viewPerson.city}" />
                          <h:outputLabel for="email" value="#{msg.Email}" />
                          <h:outputText id="email"
                                        value="#{servJPABean.viewPerson.email}" />
                          <h:outputLabel for="tel" value="#{msg.Tel}" />
                          <h:outputText id="tel" value="#{servJPABean.viewPerson.tel}" />
                      </h:panelGrid>
      
                  </h:form>
              </div>
          </ui:define>
      </ui:composition>
      </body>
      </html>
    14. Uzupełniamy brakująe wpisy w pliku messages.properties.
      siteTitleCRUD=ZTI2022 - JPA & CRUD
      welcomePageCRUD=Aplikacja CRUD z wykorzystaniem JPA i JNDI
      headCRUDappl=Aplikacja CRUD + JPA
      footerCRUDappl=ZTI 2022, Lab03 JPA, Apache TomEE 
      linkAllRecPostgresqlSidebar=Lista rekordow w PostgreSQL
      linkAddRecPostgresqlSidebar=Nowy rekord do bazy PostgreSQL
      titleFormList=Lista uzytkownikow.
      actionFormList=Operacje
      viewLinkFormList=Podglad
      editLinkFormList=Popraw
      delLinkFormList=Usun
      titleFormAdd=Edycja danych uzytkownika.
      saveLinkFormAdd=Zapisz
      titleFormUpdate=Edycja danych uzytkownika.
      saveLinkFormUpdate=Popraw
      titleFormView=Dane uzytkownika.
      viewFormCRUD=Widok
      editFormCRUD=Edycja
      delFormCRUD=Usuwanie
      
    15. Dodajemy funkcjonalność obsługującą obsługę błędów w formularzu wprowadzającym do bazy danych. Na początek wpis do pliku faces-config.xml i odpowiednie wpisy w pliku mess-errors.properties.
      		<resource-bundle>
      			<base-name>data.mess-errors</base-name>
      			<var>errors</var>
      		</resource-bundle>
      
      AddFirstname=Podanie imienia jest wymagane
      AddLastname=Podanie nazwiska jest wymagane
      AddEmail=Podanie e-mail jest wymagane
      
    16. Sprawdzamy poprawność działania aplikacji http://localhost:8080/zti_lab03 ... /viewCRUD.jsf (rys.6). Na rys.7 przedstawiono strukturę plików opracowanego projektu.
      Lab03_JPA_CRUD
      Rys.6. Opracowane zadanie CRUD wykorzystujące interfejs JPA.
      Lab03_proj
      Rys.7. Struktura plików w opracowanym projekcie.
  4. JPA - aplikacja CRUD w kontenerze Docker

    1. Uruchomienie aplikacji Jakarta EE w kontenerze Docker z wykorzystaniem JSF i JPA możliwe jest poprzez przygotowanie aplikacji zarządzającej JPA w ramach aplikacji lub poprzez odpowiednie przygotowanie serwera aplikacyjnego. W ramach naszego zadania zrealizujemy zadanie przygotowując aplikację, która sama zarządza warstwą trwałości JPA. Do realizacji zadania wykorzystamy projekt zrealizowany w poprzednim zadaniu zmieniejąc zawartość kilku plików.
    2. Wdrożenie aplikacji w środowisku systemu Linux i Windows.
      Uwaga, realizacja tego zadania jest inna dla serwera aplikacyjnego TomEE uruchamianego w środowisku Windows a inna w środowisku Linux. Przedstawione poniżej skrypty działają poprawnie w środowisku systemu Linux. Nie działają na serwerze aplikacyjnym TomEE uruchomionym w środowisku systemu Windows. Realizacja zadania w systemie Windows wymaga zmian w pliku persistence.xml i udostępnienia pliku resources.xml.
    3. Tworzymy nowy projekt zgodnie z przedstawionymi poniżej zależnościami rys.8 i 9.
      Lab03_proj_conf_1
      Rys.8. Konfiguracja projektu w ramach IDE IntelliJ.
      Lab03_proj_conf_2
      Rys.9. Konfiguracja projektu w ramach IDE IntelliJ.
    4. Do nowego projektu przekopiujemy wszystkie pliki z projektu zrealizowanego w poprzednim zadaniu, bez pliku resources.xml oraz podmieniając zawartość plików persistence.xml i PersonRepository.java.
    5. W ramach pliku pliku persistence.xml została zamieszczona informacja, że aplikacja samodzielnie będzie zarządzała warstwą trwałości, parametr transaction-type="RESOURCE_LOCAL" oraz dane wymagane do podłączenia do bazy danych.

      Plik konfiguracyjny persistence.xml - [listing dokumentu] [link do dokumentu]

        <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
      <persistence xmlns="https://jakarta.ee/xml/ns/persistence"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_2.xsd"
                   version="3.2">
          <persistence-unit name="PU_Postgresql" transaction-type="RESOURCE_LOCAL">
              <!--non-jta-data-source>java:comp/jdbc/postgres</non-jta-data-source-->
              <class>zti.model.Person</class>
              <properties>
                  <property name="jakarta.persistence.jdbc.driver" value="org.postgresql.Driver"/>
                  <property name="jakarta.persistence.jdbc.url" value=" -- jdbc url --- "/>
                  <property name="jakarta.persistence.jdbc.user" value=" -- user --- "/>
                  <property name="jakarta.persistence.jdbc.password" value=" --- password --- "/>
                  <property name="eclipselink.target-database"
                            value="org.eclipse.persistence.platform.database.PostgreSQLPlatform" />
                  <property name="eclipselink.logging.level" value="FINER" />
                  <property name="eclipselink.logging.logger" value="ServerLogger" />
              </properties>
          </persistence-unit>
      </persistence>
        
    6. W ramach pliku PersonRepository.java tworzymy lokalnego zarządcę trwałości EntityManager oraz wdrażamy obsługę transakcji w ramach menadżera trwałości (kontener jej nie obsługuje).

      Klasa PersonRepository.java - [listing dokumentu] [link do dokumentu]

        package zti.model;
      
      import jakarta.persistence.EntityManager;
      import jakarta.persistence.EntityManagerFactory;
      import jakarta.persistence.EntityTransaction;
      import jakarta.persistence.Persistence;
      import jakarta.inject.Named;
      
      import java.io.Serializable;
      import java.util.List;
      
      @Named
      //@Stateless
      
      public class PersonRepository implements Serializable {
          private EntityManager entityManager;
          private EntityTransaction tx;
          private EntityManagerFactory emf;
      
      
          public PersonRepository() {
              emf = Persistence.createEntityManagerFactory("PU_Postgresql");
              entityManager = emf.createEntityManager();
              tx = entityManager.getTransaction();
          }
      
          public List<Person> getAllPersons() {
              List<Person> people = null;
              try {
                  tx.begin();
                  people = entityManager.createQuery("select p from person p", Person.class)
                          .getResultList();
                  tx.commit();
              } catch (Exception e) {
                  System.out.println("Failed !!! " + e.getMessage());
              }
              return people;
          }
      
          public void savePerson(Person entity) {
              try {
                  tx.begin();
                  System.out.println("[ServJSF] Save entity - " + entity.getLname());
                  entityManager.persist(entity);
                  entityManager.flush();
                  tx.commit();
              } catch (Exception e) {
                  System.out.println("Failed !!! " + e.getMessage());
              }
          }
      
          public void updatePerson(Person entity) {
              try {
                  tx.begin();
                  entityManager.merge(entity);
                  tx.commit();
              } catch (Exception e) {
                  System.out.println("Failed !!! " + e.getMessage());
              }
          }
      
          public void deletePerson(Integer id) {
              try {
                  tx.begin();
                  Person p = entityManager.find(Person.class, id);
                  entityManager.remove(p);
                  entityManager.flush();
                  tx.commit();
              } catch (Exception e) {
                  System.out.println("Failed !!! " + e.getMessage());
              }
          }
      
          public Person findPerson(Person entity) {
              Person p = null;
              try {
                  tx.begin();
                  p = entityManager.find(Person.class, entity);
                  tx.commit();
              } catch (Exception e) {
                  System.out.println("Failed !!! " + e.getMessage());
              }
              return p;
          }
      
      
      }  
    7. Uruchamiamy aplikację w ramach IDEA oraz tworzymy plik zti_lab03c.war, który umieszczamy w katalogu webapps serwera Apache TomEE w kontenerze. Poprawnie przygotowana aplikacja będzie prezentowała ekran zaprezentowany na rys. 10.
      Lab03_JPA_CRUD_Docker
      Rys.10. Aplikacja CRUD uruchomiona w kontenerze Docker.
    8. Zaliczenie laboratorium

      Serwis CRUD obsługujący bazę danych
      • Opracować arkusze styli do przedstawionego powyżej projektu.
      • Dodać interfejs wyszukiwania osób po nazwisku lub po adresie e-mail,
        (możliwe częściowe podanie nazwiska lub adresu e-mail).
      • Dodatkowo, możliwość obsługi kilku jezyków.
      • Uruchomienie projektu w kontenerze i udostępnienie pod adresem "<IP-VM>:8094/zti_lab03/"
      • Przesłanie projektu w pliku *.war oraz kodu źródłowego z pliku *.zip.