Skip to content

Commit d5ed2fd

Browse files
author
Yoshio Terada
committed
Updated README.md
I added the explanation of JPA.
1 parent 79f7a1d commit d5ed2fd

1 file changed

Lines changed: 369 additions & 4 deletions

File tree

README.md

Lines changed: 369 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
## Overview of this Application
55

6-
This Java Web Application is not Microservices Application but standard Java Web Application which is wrote by using Java EE 7 technologies.
6+
This Java Web Application is not Microservices Application but standard Java Web Application which is wrote by using Java EE 8 technologies.
77

88
At first, you can select and expand the Continent (North America, Asia, Africa, Europe, South America, Oceania, Antarctica) in the left side of the menu, then you can see the countries in the Continent. Then you can select the one country, then you can see the cities where has the number of the population over 1,000,000 in right side of the screen like follows.
99
All of the data is coming from Managed [Azure Database for MySQL](https://docs.microsoft.com/azure/mysql/?WT.mc_id=docs-github-yoterada).
@@ -622,12 +622,375 @@ Azure Deployment Slot is very useful for evaluation, and you can create multiple
622622
623623
# Implement DB Access code from Web Application
624624
625-
## Explain how to implement JPA which access to MySQL
625+
## Implement JPA code which access to MySQL
626626
627-
TBD
627+
[Jakarta Persistence](https://jakarta.ee/specifications/persistence/2.2/) API is the Java API for the management of persistence and object/relational mapping framework in Jakarta EE and Java SE environments.
628628
629+
***Note
630+
Tomcat is not the Jakarta EE compiant Application Server and it doesn't support the Transaction API support. So in the Tomcat environment, you need to implement the JPA code same as Java SE environment.***
629631

630-
## Explain how to implement JSF (PrimeFaces)
632+
### Create Entity Class which map to the DB TABLE
633+
634+
I already created the following DB TABLE in MySQL.
635+
636+
```
637+
mysql> DESC `city`;
638+
+-------------+----------+------+-----+---------+----------------+
639+
| Field | Type | Null | Key | Default | Extra |
640+
+-------------+----------+------+-----+---------+----------------+
641+
| ID | int(11) | NO | PRI | NULL | auto_increment |
642+
| Name | char(35) | NO | | | |
643+
| CountryCode | char(3) | NO | MUL | | |
644+
| District | char(20) | NO | | | |
645+
| Population | int(11) | NO | | 0 | |
646+
+-------------+----------+------+-----+---------+----------------+
647+
5 rows in set (0.01 sec)
648+
649+
mysql> DESC `country`;
650+
+----------------+---------------------------------------------------------------------------------------+------+-----+---------+-------+
651+
| Field | Type | Null | Key | Default | Extra |
652+
+----------------+---------------------------------------------------------------------------------------+------+-----+---------+-------+
653+
| Code | char(3) | NO | PRI | | |
654+
| Name | char(52) | NO | | | |
655+
| Continent | enum('Asia','Europe','North America','Africa','Oceania','Antarctica','South America') | NO | | Asia | |
656+
| Region | char(26) | NO | | | |
657+
| SurfaceArea | decimal(10,2) | NO | | 0.00 | |
658+
| IndepYear | smallint(6) | YES | | NULL | |
659+
| Population | int(11) | NO | | 0 | |
660+
| LifeExpectancy | decimal(3,1) | YES | | NULL | |
661+
| GNP | decimal(10,2) | YES | | NULL | |
662+
| GNPOld | decimal(10,2) | YES | | NULL | |
663+
| LocalName | char(45) | NO | | | |
664+
| GovernmentForm | char(45) | NO | | | |
665+
| HeadOfState | char(60) | YES | | NULL | |
666+
| Capital | int(11) | YES | | NULL | |
667+
| Code2 | char(2) | NO | | | |
668+
+----------------+---------------------------------------------------------------------------------------+------+-----+---------+-------+
669+
15 rows in set (0.02 sec)
670+
```
671+
672+
In order to create the mapped class, please create following Entity class?
673+
674+
#### Country Entity Class
675+
676+
```java
677+
@Entity
678+
@Table(name = "country")
679+
@NamedQueries({
680+
@NamedQuery(name = "Country.findAll", query = "SELECT c FROM Country c"),
681+
@NamedQuery(name = "Country.findByCode", query = "SELECT c FROM Country c WHERE c.code = :code"),
682+
@NamedQuery(name = "Country.findByName", query = "SELECT c FROM Country c WHERE c.name = :name"),
683+
@NamedQuery(name = "Country.findAllContinent", query = "SELECT DISTINCT c.continent FROM Country c"),
684+
@NamedQuery(name = "Country.findByContinent", query = "SELECT c FROM Country c WHERE c.continent = :continent"),
685+
@NamedQuery(name = "Country.findByRegion", query = "SELECT c FROM Country c WHERE c.region = :region"),
686+
@NamedQuery(name = "Country.findBySurfaceArea", query = "SELECT c FROM Country c WHERE c.surfaceArea = :surfaceArea"),
687+
@NamedQuery(name = "Country.findByIndepYear", query = "SELECT c FROM Country c WHERE c.indepYear = :indepYear"),
688+
@NamedQuery(name = "Country.findByPopulation", query = "SELECT c FROM Country c WHERE c.population = :population"),
689+
@NamedQuery(name = "Country.findByLifeExpectancy", query = "SELECT c FROM Country c WHERE c.lifeExpectancy = :lifeExpectancy"),
690+
@NamedQuery(name = "Country.findByGnp", query = "SELECT c FROM Country c WHERE c.gnp = :gnp"),
691+
@NamedQuery(name = "Country.findByGNPOld", query = "SELECT c FROM Country c WHERE c.gNPOld = :gNPOld"),
692+
@NamedQuery(name = "Country.findByLocalName", query = "SELECT c FROM Country c WHERE c.localName = :localName"),
693+
@NamedQuery(name = "Country.findByGovernmentForm", query = "SELECT c FROM Country c WHERE c.governmentForm = :governmentForm"),
694+
@NamedQuery(name = "Country.findByHeadOfState", query = "SELECT c FROM Country c WHERE c.headOfState = :headOfState"),
695+
@NamedQuery(name = "Country.findByCapital", query = "SELECT c FROM Country c WHERE c.capital = :capital"),
696+
@NamedQuery(name = "Country.findByCode2", query = "SELECT c FROM Country c WHERE c.code2 = :code2")})
697+
public class Country implements Serializable {
698+
699+
private static final long serialVersionUID = 1L;
700+
@Id
701+
@Basic(optional = false)
702+
@NotNull
703+
@Size(min = 1, max = 3)
704+
@Column(name = "Code")
705+
private String code;
706+
@Basic(optional = false)
707+
@NotNull
708+
@Size(min = 1, max = 52)
709+
@Column(name = "Name")
710+
private String name;
711+
@Basic(optional = false)
712+
@NotNull
713+
@Size(min = 1, max = 13)
714+
@Column(name = "Continent")
715+
private String continent;
716+
@Basic(optional = false)
717+
@NotNull
718+
@Size(min = 1, max = 26)
719+
@Column(name = "Region")
720+
private String region;
721+
@Basic(optional = false)
722+
@NotNull
723+
@Column(name = "SurfaceArea")
724+
private BigDecimal surfaceArea;
725+
@Column(name = "IndepYear")
726+
private Short indepYear;
727+
@Basic(optional = false)
728+
@NotNull
729+
@Column(name = "Population")
730+
private int population;
731+
@Column(name = "LifeExpectancy")
732+
private BigDecimal lifeExpectancy;
733+
@Column(name = "GNP")
734+
private BigDecimal gnp;
735+
@Column(name = "GNPOld")
736+
private BigDecimal gNPOld;
737+
@Basic(optional = false)
738+
@NotNull
739+
@Size(min = 1, max = 45)
740+
@Column(name = "LocalName")
741+
private String localName;
742+
@Basic(optional = false)
743+
@NotNull
744+
@Size(min = 1, max = 45)
745+
@Column(name = "GovernmentForm")
746+
private String governmentForm;
747+
@Size(max = 60)
748+
@Column(name = "HeadOfState")
749+
private String headOfState;
750+
@Column(name = "Capital")
751+
private Integer capital;
752+
@Basic(optional = false)
753+
@NotNull
754+
@Size(min = 1, max = 2)
755+
@Column(name = "Code2")
756+
private String code2;
757+
758+
@JsonbTransient
759+
@OneToMany(cascade = CascadeType.ALL, mappedBy = "countryCode")
760+
private Collection<City> cityCollection;
761+
762+
// Setter & Getter
763+
}
764+
```
765+
766+
#### City Entity Class
767+
768+
```java
769+
@Entity
770+
@Table(name = "city")
771+
@NamedQueries({
772+
@NamedQuery(name = "City.findAll", query = "SELECT c FROM City c"),
773+
@NamedQuery(name = "City.findById", query = "SELECT c FROM City c WHERE c.id = :id"),
774+
@NamedQuery(name = "City.findByName", query = "SELECT c FROM City c WHERE c.name = :name"),
775+
@NamedQuery(name = "City.findByDistrict", query = "SELECT c FROM City c WHERE c.district = :district"),
776+
@NamedQuery(name = "City.findByPopulation", query = "SELECT c FROM City c WHERE c.population = :population"),
777+
@NamedQuery(name = "City.findOver1MillPopulation", query = "SELECT c FROM City c WHERE c.countryCode.code = :countrycode AND c.population > 1000000 ORDER BY c.population DESC")
778+
})
779+
public class City implements Serializable {
780+
781+
private static final long serialVersionUID = 1L;
782+
@Id
783+
@GeneratedValue(strategy = GenerationType.IDENTITY)
784+
@Basic(optional = false)
785+
@Column(name = "ID")
786+
private Integer id;
787+
@Basic(optional = false)
788+
@NotNull
789+
@Size(min = 1, max = 35)
790+
@Column(name = "Name")
791+
private String name;
792+
@Basic(optional = false)
793+
@NotNull
794+
@Size(min = 1, max = 20)
795+
@Column(name = "District")
796+
private String district;
797+
@Basic(optional = false)
798+
@NotNull
799+
@Column(name = "Population")
800+
private int population;
801+
@JoinColumn(name = "CountryCode", referencedColumnName = "Code")
802+
@ManyToOne(optional = false)
803+
private Country countryCode;
804+
805+
//Setter & Getter
806+
}
807+
```
808+
809+
### Create util Class for crete the EntityManagerFactory
810+
811+
Then you created the utility class to create the `EntityManagerFactory` as follows. If you are using the JPA, you can configure the `JDBC_DRIVER`, `JDBC_URL`, `DB_USER` and `DB_PASSWORD` in `persistence.xml` file by deafult.
812+
813+
However I would like to configure the info out side of the code, so I implemented to get the info from Environment Variable Value as follows.
814+
815+
So if you configure the `JDBC_DRIVER`, `JDBC_URL`, `DB_USER` and `DB_PASSWORD` on Environment Variable, you can overwrite the settings.
816+
817+
818+
```java
819+
@ApplicationScoped
820+
public class EntityManagerUtil {
821+
822+
private static final String PU_NAME = "JPAWorldDatasourcePU";
823+
@PersistenceUnit(unitName = PU_NAME)
824+
private static final EntityManagerFactory entityManagerFactory;
825+
826+
static {
827+
//You can implement the following by using MicroProfile Config.
828+
//https://github.com/eclipse/microprofile-config
829+
830+
Map<String, String> systemEnv = System.getenv();
831+
Map<String, Object> overwrite = new HashMap<>();
832+
833+
systemEnv.forEach((var key, var value) -> {
834+
switch (key) {
835+
case "JDBC_DRIVER":
836+
overwrite.put("toplink.jdbc.driver", value);
837+
overwrite.put("javax.persistence.jdbc.driver", value);
838+
break;
839+
case "JDBC_URL":
840+
overwrite.put("toplink.jdbc.url", value);
841+
overwrite.put("javax.persistence.jdbc.url", value);
842+
break;
843+
case "DB_USER":
844+
overwrite.put("toplink.jdbc.user", value);
845+
overwrite.put("javax.persistence.jdbc.user", value);
846+
break;
847+
case "DB_PASSWORD":
848+
overwrite.put("toplink.jdbc.password", value);
849+
overwrite.put("javax.persistence.jdbc.password", value);
850+
break;
851+
default:
852+
break;
853+
}
854+
});
855+
856+
try {
857+
entityManagerFactory = Persistence.createEntityManagerFactory(PU_NAME, overwrite);
858+
} catch (Throwable ex) {
859+
throw new ExceptionInInitializerError(ex);
860+
}
861+
}
862+
863+
public static EntityManager getEntityManager() {
864+
return entityManagerFactory.createEntityManager();
865+
}
866+
867+
public static void closeEntityManagerConnection() {
868+
entityManagerFactory.close();
869+
}
870+
}
871+
```
872+
873+
### Create Backend Service
874+
875+
After you created the Entity classes, you can create the backend service(busines logic).
876+
In this class, I implemented 3 methods.
877+
878+
* `findAllContinents()` : Get all continents like Asia, North America, Europe etc.
879+
* `findItemByContinent(String continent)` : Get all countries in specific continent
880+
* `findOver1MillPopulation(String countrycode)` : Get cities over 1 million population in the specific country.
881+
882+
883+
```java
884+
@RequestScoped
885+
public class CityService {
886+
887+
public List<City> findOver1MillPopulation(String countrycode) {
888+
EntityManager entityManager = EntityManagerUtil.getEntityManager();
889+
TypedQuery<City> query = entityManager.createNamedQuery("City.findOver1MillPopulation", City.class);
890+
query.setParameter("countrycode", countrycode);
891+
return query.getResultList();
892+
}
893+
894+
public List<String> findAllContinents() {
895+
EntityManager entityManager = EntityManagerUtil.getEntityManager();
896+
TypedQuery<String> query = entityManager.createNamedQuery("Country.findAllContinent", String.class);
897+
return query.getResultList();
898+
}
899+
900+
public List<Country> findItemByContinent(String continent) {
901+
EntityManager entityManager = EntityManagerUtil.getEntityManager();
902+
TypedQuery<Country> query = entityManager.createNamedQuery("Country.findByContinent", Country.class);
903+
query.setParameter("continent", continent);
904+
return query.getResultList();
905+
}
906+
```
907+
908+
In the implementation code, I used JPA Named Query to query. For example, `City.findOver1MillPopulation` in `TypedQuery<City> query = entityManager.createNamedQuery("City.findOver1MillPopulation", City.class);
909+
` which is defined in the `City` Entity class.
910+
911+
912+
## Implement the JSF (PrimeFaces) Backing Bean
913+
914+
In order to inovke the Backend Service which implemented the above from the View code, I need to implmenete the JSF `Backing Bean` as follows.
915+
916+
917+
```java
918+
@Named("countryBackingBean")
919+
@ViewScoped
920+
public class IndexBackingBean implements Serializable {
921+
922+
@Inject
923+
CityService citySvc;
924+
925+
private List<City> city;
926+
private String countrycode;
927+
private MenuModel model;
928+
929+
public List<City> getCity() {
930+
return citySvc.findOver1MillPopulation(countrycode);
931+
}
932+
933+
public MenuModel getModel() {
934+
return model;
935+
}
936+
937+
@PostConstruct
938+
public void init() {
939+
model = new DefaultMenuModel();
940+
941+
//First submenu
942+
DefaultSubMenu firstSubmenu = new DefaultSubMenu();
943+
firstSubmenu.setLabel("Select Country");
944+
945+
getAllContinents().stream().forEach(continents -> {
946+
DefaultSubMenu submenue = DefaultSubMenu
947+
.builder()
948+
.id(continents)
949+
.expanded(true)
950+
.label(continents)
951+
.icon("pi pi-home")
952+
.elements(createSecondMenue(continents))
953+
.build();
954+
firstSubmenu.getElements().add(submenue);
955+
});
956+
model.getElements().add(firstSubmenu);
957+
958+
}
959+
960+
private List<MenuElement> createSecondMenue(String continent) {
961+
List<Country> countryInContinents = getCountryInContinents(continent);
962+
return countryInContinents.stream().map((var country) -> {
963+
String countryName = country.getName();
964+
countrycode = country.getCode();
965+
Map<String, List<String>> param = new HashMap<>();
966+
param.put("countrycode", Arrays.asList(countrycode));
967+
968+
return DefaultMenuItem
969+
.builder()
970+
.ajax(true)
971+
.command("#{countryBackingBean.selectMenueOfCountry}")
972+
.update(":mainform:citydata")
973+
.value(countryName)
974+
.params(param)
975+
.build();
976+
}).collect(Collectors.toList());
977+
}
978+
979+
public List<String> getAllContinents() {
980+
return citySvc.findAllContinents();
981+
}
982+
983+
public List<Country> getCountryInContinents(String continent) {
984+
return citySvc.findItemByContinent(continent);
985+
}
986+
987+
public void selectMenueOfCountry(MenuActionEvent event) {
988+
countrycode = event.getMenuItem().getParams().get("countrycode").get(0);
989+
}
990+
}
991+
```
992+
993+
## Implement the JSF (PrimeFaces) View
631994

632995
[JavaServerFaces (JSF)](https://javaee.github.io/javaserverfaces-spec/) is the standard component-oriented user interface (UI) framework for the Java EE platform. JSF is included in the Java EE platform. JSF works equally as well as a standalone web framework.
633996

@@ -737,6 +1100,8 @@ In order to use it, I added the `p:dataTable` tag in index.xtml.
7371100

7381101
### Following is the Created index.xhtml
7391102

1103+
![](./images/screenshot.jpg)
1104+
7401105
```xml
7411106
<!DOCTYPE html>
7421107
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://xmlns.jcp.org/jsf/core"

0 commit comments

Comments
 (0)