|
3 | 3 |
|
4 | 4 | ## Overview of this Application |
5 | 5 |
|
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. |
7 | 7 |
|
8 | 8 | 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. |
9 | 9 | 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 |
622 | 622 |
|
623 | 623 | # Implement DB Access code from Web Application |
624 | 624 |
|
625 | | -## Explain how to implement JPA which access to MySQL |
| 625 | +## Implement JPA code which access to MySQL |
626 | 626 |
|
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. |
628 | 628 |
|
| 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.*** |
629 | 631 |
|
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 |
631 | 994 |
|
632 | 995 | [JavaServer™ Faces (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. |
633 | 996 |
|
@@ -737,6 +1100,8 @@ In order to use it, I added the `p:dataTable` tag in index.xtml. |
737 | 1100 |
|
738 | 1101 | ### Following is the Created index.xhtml |
739 | 1102 |
|
| 1103 | + |
| 1104 | + |
740 | 1105 | ```xml |
741 | 1106 | <!DOCTYPE html> |
742 | 1107 | <html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://xmlns.jcp.org/jsf/core" |
|
0 commit comments