Создание веб-приложения для корзины покупок с помощью Spring Boot, Hibernate
1. Обзор приложения
В данной статье я покажу вам как создать веб приложение корзины (shopping cart) с Spring Boot, Hibernate и Thymeleaf.
Приложение основано на:
Eclipse 4.7 (Oxygen)
Spring Boot 2.x
Hibernate 5.x
Hibernate и JPA являются 2-мя похожими технологиями. Если вы знаете Hibernate вы можете легко работать с JPA и наоборот. Но JPA не так хорошо поддерживает пагинацию (Pagination). При этом пагинация это необходиме свойство в приложении, в этом Hibernate поддерживает лучше, имеено это причина почему я использую Hibernate для данного приложения.
2. Просмотр приложения
Функция покупки для пользователя и без требования входа в систему.
Функция для сотрудгиков и менеджеров:
Функция добавления и редактирования информации продукта (Только для менеджеров).
3. Приготовить базу данных
MySQL
-- Create table
create table ACCOUNTS
(
USER_NAME VARCHAR(20) not null,
ACTIVE BIT not null,
ENCRYTED_PASSWORD VARCHAR(128) not null,
USER_ROLE VARCHAR(20) not null
) ;
alter table ACCOUNTS
add primary key (USER_NAME) ;
---------------------------------------
create table PRODUCTS
(
CODE VARCHAR(20) not null,
IMAGE longblob,
NAME VARCHAR(255) not null,
PRICE double precision not null,
CREATE_DATE datetime not null
) ;
alter table PRODUCTS
add primary key (CODE) ;
---------------------------------------
-- Create table
create table ORDERS
(
ID VARCHAR(50) not null,
AMOUNT double precision not null,
CUSTOMER_ADDRESS VARCHAR(255) not null,
CUSTOMER_EMAIL VARCHAR(128) not null,
CUSTOMER_NAME VARCHAR(255) not null,
CUSTOMER_PHONE VARCHAR(128) not null,
ORDER_DATE datetime not null,
ORDER_NUM INTEGER not null
) ;
alter table ORDERS
add primary key (ID) ;
alter table ORDERS
add constraint ORDER_UK unique (ORDER_NUM) ;
---------------------------------------
-- Create table
create table ORDER_DETAILS
(
ID VARCHAR(50) not null,
AMOUNT double precision not null,
PRICE double precision not null,
QUANITY INTEGER not null,
ORDER_ID VARCHAR(50) not null,
PRODUCT_ID VARCHAR(20) not null
) ;
--
alter table ORDER_DETAILS
add primary key (ID) ;
alter table ORDER_DETAILS
add constraint ORDER_DETAIL_ORD_FK foreign key (ORDER_ID)
references ORDERS (ID);
alter table ORDER_DETAILS
add constraint ORDER_DETAIL_PROD_FK foreign key (PRODUCT_ID)
references PRODUCTS (CODE);
---------------------------------------
insert into Accounts (USER_NAME, ACTIVE, ENCRYTED_PASSWORD, USER_ROLE)
values ('employee1', 1,
'$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 'ROLE_EMPLOYEE');
insert into Accounts (USER_NAME, ACTIVE, ENCRYTED_PASSWORD, USER_ROLE)
values ('manager1', 1,
'$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 'ROLE_MANAGER');
----------------
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S001', 'Core Java', 100, sysdate);
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S002', 'Spring for Beginners', 50, sysdate);
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S003', 'Swift for Beginners', 120, sysdate);
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S004', 'Oracle XML Parser', 120, sysdate);
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S005', 'CSharp Tutorial for Beginers', 110, sysdate);
SQL Server
-- Create table
create table ACCOUNTS
(
USER_NAME VARCHAR(20) not null,
ACTIVE BIT not null,
ENCRYTED_PASSWORD VARCHAR(128) not null,
USER_ROLE VARCHAR(20) not null
) ;
alter table ACCOUNTS
add primary key (USER_NAME) ;
---------------------------------------
create table PRODUCTS
(
CODE VARCHAR(20) not null,
IMAGE image,
NAME VARCHAR(255) not null,
PRICE double precision not null,
CREATE_DATE datetime not null
) ;
alter table PRODUCTS
add primary key (CODE) ;
---------------------------------------
-- Create table
create table ORDERS
(
ID VARCHAR(50) not null,
AMOUNT double precision not null,
CUSTOMER_ADDRESS VARCHAR(255) not null,
CUSTOMER_EMAIL VARCHAR(128) not null,
CUSTOMER_NAME VARCHAR(255) not null,
CUSTOMER_PHONE VARCHAR(128) not null,
ORDER_DATE datetime not null,
ORDER_NUM INT not null
) ;
alter table ORDERS
add primary key (ID) ;
alter table ORDERS
add constraint ORDER_UK unique (ORDER_NUM) ;
---------------------------------------
-- Create table
create table ORDER_DETAILS
(
ID VARCHAR(50) not null,
AMOUNT double precision not null,
PRICE double precision not null,
QUANITY INT not null,
ORDER_ID VARCHAR(50) not null,
PRODUCT_ID VARCHAR(20) not null
) ;
--
alter table ORDER_DETAILS
add primary key (ID) ;
alter table ORDER_DETAILS
add constraint ORDER_DETAIL_ORD_FK foreign key (ORDER_ID)
references ORDERS (ID);
alter table ORDER_DETAILS
add constraint ORDER_DETAIL_PROD_FK foreign key (PRODUCT_ID)
references PRODUCTS (CODE);
---------------------------------------
insert into Accounts (USER_NAME, ACTIVE, ENCRYTED_PASSWORD, USER_ROLE)
values ('employee1', 1,
'$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 'ROLE_EMPLOYEE');
insert into Accounts (USER_NAME, ACTIVE, ENCRYTED_PASSWORD, USER_ROLE)
values ('manager1', 1,
'$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 'ROLE_MANAGER');
----------------
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S001', 'Core Java', 100, CURRENT_TIMESTAMP );
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S002', 'Spring for Beginners', 50, CURRENT_TIMESTAMP );
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S003', 'Swift for Beginners', 120, CURRENT_TIMESTAMP );
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S004', 'Oracle XML Parser', 120, CURRENT_TIMESTAMP );
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S005', 'CSharp Tutorial for Beginers', 110, CURRENT_TIMESTAMP );
Postgres
-- Create table
create table ACCOUNTS
(
USER_NAME VARCHAR(20) not null,
ACTIVE BOOLEAN not null,
ENCRYTED_PASSWORD VARCHAR(128) not null,
USER_ROLE VARCHAR(20) not null
) ;
alter table ACCOUNTS
add primary key (USER_NAME) ;
---------------------------------------
create table PRODUCTS
(
CODE VARCHAR(20) not null,
IMAGE bytea,
NAME VARCHAR(255) not null,
PRICE double precision not null,
CREATE_DATE Timestamp without time zone not null
) ;
alter table PRODUCTS
add primary key (CODE) ;
---------------------------------------
-- Create table
create table ORDERS
(
ID VARCHAR(50) not null,
AMOUNT double precision not null,
CUSTOMER_ADDRESS VARCHAR(255) not null,
CUSTOMER_EMAIL VARCHAR(128) not null,
CUSTOMER_NAME VARCHAR(255) not null,
CUSTOMER_PHONE VARCHAR(128) not null,
ORDER_DATE Timestamp without time zone not null,
ORDER_NUM INT not null
) ;
alter table ORDERS
add primary key (ID) ;
alter table ORDERS
add constraint ORDER_UK unique (ORDER_NUM) ;
---------------------------------------
-- Create table
create table ORDER_DETAILS
(
ID VARCHAR(50) not null,
AMOUNT double precision not null,
PRICE double precision not null,
QUANITY INT not null,
ORDER_ID VARCHAR(50) not null,
PRODUCT_ID VARCHAR(20) not null
) ;
--
alter table ORDER_DETAILS
add primary key (ID) ;
alter table ORDER_DETAILS
add constraint ORDER_DETAIL_ORD_FK foreign key (ORDER_ID)
references ORDERS (ID);
alter table ORDER_DETAILS
add constraint ORDER_DETAIL_PROD_FK foreign key (PRODUCT_ID)
references PRODUCTS (CODE);
---------------------------------------
insert into Accounts (USER_NAME, ACTIVE, ENCRYTED_PASSWORD, USER_ROLE)
values ('employee1', true,
'$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 'ROLE_EMPLOYEE');
insert into Accounts (USER_NAME, ACTIVE, ENCRYTED_PASSWORD, USER_ROLE)
values ('manager1', true,
'$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 'ROLE_MANAGER');
----------------
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S001', 'Core Java', 100, current_timestamp);
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S002', 'Spring for Beginners', 50, current_timestamp);
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S003', 'Swift for Beginners', 120, current_timestamp);
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S004', 'Oracle XML Parser', 120, current_timestamp);
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S005', 'CSharp Tutorial for Beginers', 110, current_timestamp);
Oracle
-- Create table
create table ACCOUNTS
(
USER_NAME VARCHAR2(20 CHAR) not null,
ACTIVE NUMBER(1) not null,
ENCRYTED_PASSWORD VARCHAR2(128 CHAR) not null,
USER_ROLE VARCHAR2(20) not null
) ;
alter table ACCOUNTS
add primary key (USER_NAME) ;
---------------------------------------
create table PRODUCTS
(
CODE VARCHAR2(20 CHAR) not null,
IMAGE BLOB,
NAME VARCHAR2(255 CHAR) not null,
PRICE FLOAT not null,
CREATE_DATE DATE default sysdate not null
) ;
alter table PRODUCTS
add primary key (CODE) ;
---------------------------------------
-- Create table
create table ORDERS
(
ID VARCHAR2(50 CHAR) not null,
AMOUNT FLOAT not null,
CUSTOMER_ADDRESS VARCHAR2(255 CHAR) not null,
CUSTOMER_EMAIL VARCHAR2(128 CHAR) not null,
CUSTOMER_NAME VARCHAR2(255 CHAR) not null,
CUSTOMER_PHONE VARCHAR2(128 CHAR) not null,
ORDER_DATE TIMESTAMP(6) not null,
ORDER_NUM NUMBER(10) not null
) ;
alter table ORDERS
add primary key (ID) ;
alter table ORDERS
add constraint ORDER_UK unique (ORDER_NUM) ;
---------------------------------------
-- Create table
create table ORDER_DETAILS
(
ID VARCHAR2(50 CHAR) not null,
AMOUNT FLOAT not null,
PRICE FLOAT not null,
QUANITY NUMBER(10) not null,
ORDER_ID VARCHAR2(50 CHAR) not null,
PRODUCT_ID VARCHAR2(20 CHAR) not null
) ;
--
alter table ORDER_DETAILS
add primary key (ID) ;
alter table ORDER_DETAILS
add constraint ORDER_DETAIL_ORD_FK foreign key (ORDER_ID)
references ORDERS (ID);
alter table ORDER_DETAILS
add constraint ORDER_DETAIL_PROD_FK foreign key (PRODUCT_ID)
references PRODUCTS (CODE);
---------------------------------------
insert into Accounts (USER_NAME, ACTIVE, ENCRYTED_PASSWORD, USER_ROLE)
values ('employee1', 1,
'$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 'ROLE_EMPLOYEE');
insert into Accounts (USER_NAME, ACTIVE, ENCRYTED_PASSWORD, USER_ROLE)
values ('manager1', 1,
'$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 'ROLE_MANAGER');
----------------
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S001', 'Core Java', 100, sysdate);
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S002', 'Spring for Beginners', 50, sysdate);
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S003', 'Swift for Beginners', 120, sysdate);
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S004', 'Oracle XML Parser', 120, sysdate);
insert into products (CODE, NAME, PRICE, CREATE_DATE)
values ('S005', 'CSharp Tutorial for Beginers', 110, sysdate);
Commit;
4. Создать проект Spring Boot
На Eclipse создать проект Spring Boot.
Ввести:
- Name: SbHibernateShoppingCart
- Group: org.o7planning
- Description: Shopping Cart + Spring Boot + Hibernate + Thymeleaf
- Package: org.o7plannng.sbshoppingcart
Выбрать технологии и библиотеки, которые будут использоваться:
- Security
- JPA
- Thymeleaf
- Web
- MySQL
- SQL Server
- PosgreSQL
Так как данная статья является инструкцией, я выберу библиотеки для 4 типов баз данных это Oracle, My SQL, SQL Server, PostgreSQL. С Oracle мы объявим для нее библиотеку в следующем шаге (В части конфигурации pom.xml).
Когда вы выбираете JPA, он уже включает библиотеки JPA и Hibernate.
5. Конфигурация pom.xml
Если вы используете базу данных Oracle, вам нужно объявить необходимую библиотеку для Oracle в файле pom.xml:
** Oracle **
<dependencies>
.....
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
.....
</dependencies>
<repositories>
....
<!-- Repository for ORACLE JDBC Driver -->
<repository>
<id>codelds</id>
<url>https://code.lds.org/nexus/content/groups/main-repo</url>
</repository>
.....
</repositories>
Если вы подключаетесь к базе данных SQL Service, вы можете использовать одну из 2-х библиотек JTDS или Mssql-Jdbc:
** SQL Server **
<dependencies>
.....
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.sourceforge.jtds</groupId>
<artifactId>jtds</artifactId>
<scope>runtime</scope>
</dependency>
.....
</dependencies>
Другие библиотеки:
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<!-- Commons Email validator,... -->
<!-- http://mvnrepository.com/artifact/commons-validator/commons-validator -->
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.threeten/threetenbp -->
<dependency>
<groupId>org.threeten</groupId>
<artifactId>threetenbp</artifactId>
<version>1.3.6</version>
</dependency>
Полное содержание pom.xml:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.o7planning</groupId>
<artifactId>SbHibernateShoppingCart</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SbHibernateShoppingCart</name>
<description>Shopping Cart + Spring Boot + Hibernate + Thymeleaf</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
<!-- SQLServer JDBC driver (JTDS) -->
<!-- http://mvnrepository.com/artifact/net.sourceforge.jtds/jtds -->
<dependency>
<groupId>net.sourceforge.jtds</groupId>
<artifactId>jtds</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<!-- Commons Email validator,... -->
<!-- http://mvnrepository.com/artifact/commons-validator/commons-validator -->
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.threeten/threetenbp -->
<dependency>
<groupId>org.threeten</groupId>
<artifactId>threetenbp</artifactId>
<version>1.3.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<!-- Repository for ORACLE JDBC Driver -->
<repository>
<id>codelds</id>
<url>https://code.lds.org/nexus/content/groups/main-repo</url>
</repository>
</repositories>
</project>
6. Конфигурация Hibernate
application.properties (MySQL)
# ===============================
# DATABASE
# ===============================
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
spring.datasource.username=root
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
application.properties (SQL Server + Mssql-Jdbc Driver)
# ===============================
# DATABASE
# ===============================
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.url=jdbc:sqlserver://tran-vmware-pc\\SQLEXPRESS:1433;databaseName=shoppingcart
spring.datasource.username=sa
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServerDialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
application.properties (SQL Server + JTDS Driver)
# ===============================
# DATABASE
# ===============================
spring.datasource.driver-class-name=net.sourceforge.jtds.jdbc.Driver
spring.datasource.url=jdbc:jtds:sqlserver://tran-vmware-pc:1433/shoppingcart;instance=SQLEXPRESS
spring.datasource.username=sa
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServerDialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
application.properties (ORACLE)
# ===============================
# DATABASE
# ===============================
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@tran-vmware-pc:1521:db12c
spring.datasource.username=shoppingcart
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
application.properties (Postgres)
# ===============================
# DATABASE
# ===============================
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://tran-vmware-pc:5432/shoppingcart
spring.datasource.username=postgres
spring.datasource.password=12345
# ===============================
# JPA / HIBERNATE
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL82Dialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
# Fix Postgres JPA Error:
# Method org.postgresql.jdbc.PgConnection.createClob() is not yet implemented.
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
Примечание Spring Boot по умолчанию автоматически конфигурирует JPA, и создает Spring BEAN связанные с JPA, эти автоматические конфигурации Spring Boot включают:
- DataSourceAutoConfiguration
- DataSourceTransactionManagerAutoConfiguration
- HibernateJpaAutoConfiguration
Цель данного приложения использовать Hibernate, поэтому нам нужно отключить автоматические конфигурации у Spring Boot.сказанные выше.
** SbHibernateShoppingCartApplication **
package org.o7planning.sbshoppingcart;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
@SpringBootApplication
@EnableAutoConfiguration(exclude = { //
DataSourceAutoConfiguration.class, //
DataSourceTransactionManagerAutoConfiguration.class, //
HibernateJpaAutoConfiguration.class })
public class SbHibernateShoppingCartApplication {
public static void main(String[] args) {
SpringApplication.run(SbHibernateShoppingCartApplication.class, args);
}
........
}
После, конфигурировать необходимые Spring BEAN для Hibernate.
SbHibernateShoppingCartApplication.java
package org.o7planning.sbshoppingcart;
import java.util.Properties;
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
@SpringBootApplication
@EnableAutoConfiguration(exclude = { //
DataSourceAutoConfiguration.class, //
DataSourceTransactionManagerAutoConfiguration.class, //
HibernateJpaAutoConfiguration.class })
public class SbHibernateShoppingCartApplication {
@Autowired
private Environment env;
public static void main(String[] args) {
SpringApplication.run(SbHibernateShoppingCartApplication.class, args);
}
@Bean(name = "dataSource")
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
// See: application.properties
dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
System.out.println("## getDataSource: " + dataSource);
return dataSource;
}
@Autowired
@Bean(name = "sessionFactory")
public SessionFactory getSessionFactory(DataSource dataSource) throws Exception {
Properties properties = new Properties();
// See: application.properties
properties.put("hibernate.dialect", env.getProperty("spring.jpa.properties.hibernate.dialect"));
properties.put("hibernate.show_sql", env.getProperty("spring.jpa.show-sql"));
properties.put("current_session_context_class", //
env.getProperty("spring.jpa.properties.hibernate.current_session_context_class"));
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
// Package contain entity classes
factoryBean.setPackagesToScan(new String[] { "" });
factoryBean.setDataSource(dataSource);
factoryBean.setHibernateProperties(properties);
factoryBean.afterPropertiesSet();
//
SessionFactory sf = factoryBean.getObject();
System.out.println("## getSessionFactory: " + sf);
return sf;
}
@Autowired
@Bean(name = "transactionManager")
public HibernateTransactionManager getTransactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = new HibernateTransactionManager(sessionFactory);
return transactionManager;
}
}
7. Безопасность и UserDetailsService
В данном приложении у нас есть 2 роли ROLE_MANAGER и ROLE_EMPLOYEE.
- ROLE_EMPLOYEE: Данная роль для сотрудников, пользователь с данной ролью может смотреть список заказов, детали заказов, и смотреть их профайлы.
- ROLE_MANAGER: Данная роль для менеджеров, пользователь с данной ролью может смотреть список заказов, детали заказов, и смотреть их профайлы, добавлять и редактировать продукт.
WebSecurityConfig.java
package org.o7planning.sbshoppingcart.config;
import org.o7planning.sbshoppingcart.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsServiceImpl userDetailsService;
@Bean
public BCryptPasswordEncoder passwordEncoder() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
return bCryptPasswordEncoder;
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// Setting Service to find User in the database.
// And Setting PassswordEncoder
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
// Requires login with role ROLE_EMPLOYEE or ROLE_MANAGER.
// If not, it will redirect to /admin/login.
http.authorizeRequests().antMatchers("/admin/orderList", "/admin/order", "/admin/accountInfo")//
.access("hasAnyRole('ROLE_EMPLOYEE', 'ROLE_MANAGER')");
// Pages only for MANAGER
http.authorizeRequests().antMatchers("/admin/product").access("hasRole('ROLE_MANAGER')");
// When user login, role XX.
// But access to the page requires the YY role,
// An AccessDeniedException will be thrown.
http.authorizeRequests().and().exceptionHandling().accessDeniedPage("/403");
// Configuration for Login Form.
http.authorizeRequests().and().formLogin()//
//
.loginProcessingUrl("/j_spring_security_check") // Submit URL
.loginPage("/admin/login")//
.defaultSuccessUrl("/admin/accountInfo")//
.failureUrl("/admin/login?error=true")//
.usernameParameter("userName")//
.passwordParameter("password")
// Configuration for the Logout page.
// (After logout, go to home page)
.and().logout().logoutUrl("/admin/logout").logoutSuccessUrl("/");
}
}
Класс UserDetailsServiceImpl выполняет интерфейс UserDetailsService, он используется для поиска пользователя в базе данных соответствуюего имени пользователя и для настройки роли данному пользователю.
UserDetailsServiceImpl.java
package org.o7planning.sbshoppingcart.service;
import java.util.ArrayList;
import java.util.List;
import org.o7planning.sbshoppingcart.dao.AccountDAO;
import org.o7planning.sbshoppingcart.entity.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private AccountDAO accountDAO;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountDAO.findAccount(username);
System.out.println("Account= " + account);
if (account == null) {
throw new UsernameNotFoundException("User " //
+ username + " was not found in the database");
}
// EMPLOYEE,MANAGER,..
String role = account.getUserRole();
List<GrantedAuthority> grantList = new ArrayList<GrantedAuthority>();
// ROLE_EMPLOYEE, ROLE_MANAGER
GrantedAuthority authority = new SimpleGrantedAuthority(role);
grantList.add(authority);
boolean enabled = account.isActive();
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
UserDetails userDetails = (UserDetails) new User(account.getUserName(), //
account.getEncrytedPassword(), enabled, accountNonExpired, //
credentialsNonExpired, accountNonLocked, grantList);
return userDetails;
}
}
8. Классы Entity
Account.java
package org.o7planning.sbshoppingcart.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "Accounts")
public class Account implements Serializable {
private static final long serialVersionUID = -2054386655979281969L;
public static final String ROLE_MANAGER = "MANAGER";
public static final String ROLE_EMPLOYEE = "EMPLOYEE";
@Id
@Column(name = "User_Name", length = 20, nullable = false)
private String userName;
@Column(name = "Encryted_Password", length = 128, nullable = false)
private String encrytedPassword;
@Column(name = "Active", length = 1, nullable = false)
private boolean active;
@Column(name = "User_Role", length = 20, nullable = false)
private String userRole;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getEncrytedPassword() {
return encrytedPassword;
}
public void setEncrytedPassword(String encrytedPassword) {
this.encrytedPassword = encrytedPassword;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
public String getUserRole() {
return userRole;
}
public void setUserRole(String userRole) {
this.userRole = userRole;
}
@Override
public String toString() {
return "[" + this.userName + "," + this.encrytedPassword + "," + this.userRole + "]";
}
}
Product.java
package org.o7planning.sbshoppingcart.entity;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Entity
@Table(name = "Products")
public class Product implements Serializable {
private static final long serialVersionUID = -1000119078147252957L;
@Id
@Column(name = "Code", length = 20, nullable = false)
private String code;
@Column(name = "Name", length = 255, nullable = false)
private String name;
@Column(name = "Price", nullable = false)
private double price;
@Lob
@Column(name = "Image", length = Integer.MAX_VALUE, nullable = true)
private byte[] image;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "Create_Date", nullable = false)
private Date createDate;
public Product() {
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public byte[] getImage() {
return image;
}
public void setImage(byte[] image) {
this.image = image;
}
}
Order.java
package org.o7planning.sbshoppingcart.entity;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@Entity
@Table(name = "Orders", //
uniqueConstraints = { @UniqueConstraint(columnNames = "Order_Num") })
public class Order implements Serializable {
private static final long serialVersionUID = -2576670215015463100L;
@Id
@Column(name = "ID", length = 50)
private String id;
@Column(name = "Order_Date", nullable = false)
private Date orderDate;
@Column(name = "Order_Num", nullable = false)
private int orderNum;
@Column(name = "Amount", nullable = false)
private double amount;
@Column(name = "Customer_Name", length = 255, nullable = false)
private String customerName;
@Column(name = "Customer_Address", length = 255, nullable = false)
private String customerAddress;
@Column(name = "Customer_Email", length = 128, nullable = false)
private String customerEmail;
@Column(name = "Customer_Phone", length = 128, nullable = false)
private String customerPhone;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Date getOrderDate() {
return orderDate;
}
public void setOrderDate(Date orderDate) {
this.orderDate = orderDate;
}
public int getOrderNum() {
return orderNum;
}
public void setOrderNum(int orderNum) {
this.orderNum = orderNum;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getCustomerAddress() {
return customerAddress;
}
public void setCustomerAddress(String customerAddress) {
this.customerAddress = customerAddress;
}
public String getCustomerEmail() {
return customerEmail;
}
public void setCustomerEmail(String customerEmail) {
this.customerEmail = customerEmail;
}
public String getCustomerPhone() {
return customerPhone;
}
public void setCustomerPhone(String customerPhone) {
this.customerPhone = customerPhone;
}
}
OrderDetail.java
package org.o7planning.sbshoppingcart.entity;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "Order_Details")
public class OrderDetail implements Serializable {
private static final long serialVersionUID = 7550745928843183535L;
@Id
@Column(name = "ID", length = 50, nullable = false)
private String id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ORDER_ID", nullable = false, //
foreignKey = @ForeignKey(name = "ORDER_DETAIL_ORD_FK"))
private Order order;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "PRODUCT_ID", nullable = false, //
foreignKey = @ForeignKey(name = "ORDER_DETAIL_PROD_FK"))
private Product product;
@Column(name = "Quanity", nullable = false)
private int quanity;
@Column(name = "Price", nullable = false)
private double price;
@Column(name = "Amount", nullable = false)
private double amount;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
public int getQuanity() {
return quanity;
}
public void setQuanity(int quanity) {
this.quanity = quanity;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
}
9. Классы DAO
AccountDAO.java
package org.o7planning.sbshoppingcart.dao;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.o7planning.sbshoppingcart.entity.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Transactional
@Repository
public class AccountDAO {
@Autowired
private SessionFactory sessionFactory;
public Account findAccount(String userName) {
Session session = this.sessionFactory.getCurrentSession();
return session.find(Account.class, userName);
}
}
OrderDAO.java
package org.o7planning.sbshoppingcart.dao;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
import org.o7planning.sbshoppingcart.entity.Order;
import org.o7planning.sbshoppingcart.entity.OrderDetail;
import org.o7planning.sbshoppingcart.entity.Product;
import org.o7planning.sbshoppingcart.model.CartInfo;
import org.o7planning.sbshoppingcart.model.CartLineInfo;
import org.o7planning.sbshoppingcart.model.CustomerInfo;
import org.o7planning.sbshoppingcart.model.OrderDetailInfo;
import org.o7planning.sbshoppingcart.model.OrderInfo;
import org.o7planning.sbshoppingcart.pagination.PaginationResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Transactional
@Repository
public class OrderDAO {
@Autowired
private SessionFactory sessionFactory;
@Autowired
private ProductDAO productDAO;
private int getMaxOrderNum() {
String sql = "Select max(o.orderNum) from " + Order.class.getName() + " o ";
Session session = this.sessionFactory.getCurrentSession();
Query<Integer> query = session.createQuery(sql, Integer.class);
Integer value = (Integer) query.getSingleResult();
if (value == null) {
return 0;
}
return value;
}
@Transactional(rollbackFor = Exception.class)
public void saveOrder(CartInfo cartInfo) {
Session session = this.sessionFactory.getCurrentSession();
int orderNum = this.getMaxOrderNum() + 1;
Order order = new Order();
order.setId(UUID.randomUUID().toString());
order.setOrderNum(orderNum);
order.setOrderDate(new Date());
order.setAmount(cartInfo.getAmountTotal());
CustomerInfo customerInfo = cartInfo.getCustomerInfo();
order.setCustomerName(customerInfo.getName());
order.setCustomerEmail(customerInfo.getEmail());
order.setCustomerPhone(customerInfo.getPhone());
order.setCustomerAddress(customerInfo.getAddress());
session.persist(order);
List<CartLineInfo> lines = cartInfo.getCartLines();
for (CartLineInfo line : lines) {
OrderDetail detail = new OrderDetail();
detail.setId(UUID.randomUUID().toString());
detail.setOrder(order);
detail.setAmount(line.getAmount());
detail.setPrice(line.getProductInfo().getPrice());
detail.setQuanity(line.getQuantity());
String code = line.getProductInfo().getCode();
Product product = this.productDAO.findProduct(code);
detail.setProduct(product);
session.persist(detail);
}
// Order Number!
cartInfo.setOrderNum(orderNum);
// Flush
session.flush();
}
// @page = 1, 2, ...
public PaginationResult<OrderInfo> listOrderInfo(int page, int maxResult, int maxNavigationPage) {
String sql = "Select new " + OrderInfo.class.getName()//
+ "(ord.id, ord.orderDate, ord.orderNum, ord.amount, "
+ " ord.customerName, ord.customerAddress, ord.customerEmail, ord.customerPhone) " + " from "
+ Order.class.getName() + " ord "//
+ " order by ord.orderNum desc";
Session session = this.sessionFactory.getCurrentSession();
Query<OrderInfo> query = session.createQuery(sql, OrderInfo.class);
return new PaginationResult<OrderInfo>(query, page, maxResult, maxNavigationPage);
}
public Order findOrder(String orderId) {
Session session = this.sessionFactory.getCurrentSession();
return session.find(Order.class, orderId);
}
public OrderInfo getOrderInfo(String orderId) {
Order order = this.findOrder(orderId);
if (order == null) {
return null;
}
return new OrderInfo(order.getId(), order.getOrderDate(), //
order.getOrderNum(), order.getAmount(), order.getCustomerName(), //
order.getCustomerAddress(), order.getCustomerEmail(), order.getCustomerPhone());
}
public List<OrderDetailInfo> listOrderDetailInfos(String orderId) {
String sql = "Select new " + OrderDetailInfo.class.getName() //
+ "(d.id, d.product.code, d.product.name , d.quanity,d.price,d.amount) "//
+ " from " + OrderDetail.class.getName() + " d "//
+ " where d.order.id = :orderId ";
Session session = this.sessionFactory.getCurrentSession();
Query<OrderDetailInfo> query = session.createQuery(sql, OrderDetailInfo.class);
query.setParameter("orderId", orderId);
return query.getResultList();
}
}
ProductDAO.java
package org.o7planning.sbshoppingcart.dao;
import java.io.IOException;
import java.util.Date;
import javax.persistence.NoResultException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
import org.o7planning.sbshoppingcart.entity.Product;
import org.o7planning.sbshoppingcart.form.ProductForm;
import org.o7planning.sbshoppingcart.model.ProductInfo;
import org.o7planning.sbshoppingcart.pagination.PaginationResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Transactional
@Repository
public class ProductDAO {
@Autowired
private SessionFactory sessionFactory;
public Product findProduct(String code) {
try {
String sql = "Select e from " + Product.class.getName() + " e Where e.code =:code ";
Session session = this.sessionFactory.getCurrentSession();
Query<Product> query = session.createQuery(sql, Product.class);
query.setParameter("code", code);
return (Product) query.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
public ProductInfo findProductInfo(String code) {
Product product = this.findProduct(code);
if (product == null) {
return null;
}
return new ProductInfo(product.getCode(), product.getName(), product.getPrice());
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void save(ProductForm productForm) {
Session session = this.sessionFactory.getCurrentSession();
String code = productForm.getCode();
Product product = null;
boolean isNew = false;
if (code != null) {
product = this.findProduct(code);
}
if (product == null) {
isNew = true;
product = new Product();
product.setCreateDate(new Date());
}
product.setCode(code);
product.setName(productForm.getName());
product.setPrice(productForm.getPrice());
if (productForm.getFileData() != null) {
byte[] image = null;
try {
image = productForm.getFileData().getBytes();
} catch (IOException e) {
}
if (image != null && image.length > 0) {
product.setImage(image);
}
}
if (isNew) {
session.persist(product);
}
// If error in DB, Exceptions will be thrown out immediately
session.flush();
}
public PaginationResult<ProductInfo> queryProducts(int page, int maxResult, int maxNavigationPage,
String likeName) {
String sql = "Select new " + ProductInfo.class.getName() //
+ "(p.code, p.name, p.price) " + " from "//
+ Product.class.getName() + " p ";
if (likeName != null && likeName.length() > 0) {
sql += " Where lower(p.name) like :likeName ";
}
sql += " order by p.createDate desc ";
//
Session session = this.sessionFactory.getCurrentSession();
Query<ProductInfo> query = session.createQuery(sql, ProductInfo.class);
if (likeName != null && likeName.length() > 0) {
query.setParameter("likeName", "%" + likeName.toLowerCase() + "%");
}
return new PaginationResult<ProductInfo>(query, page, maxResult, maxNavigationPage);
}
public PaginationResult<ProductInfo> queryProducts(int page, int maxResult, int maxNavigationPage) {
return queryProducts(page, maxResult, maxNavigationPage, null);
}
}
10. Классы Model
CartInfo.java
package org.o7planning.sbshoppingcart.model;
import java.util.ArrayList;
import java.util.List;
public class CartInfo {
private int orderNum;
private CustomerInfo customerInfo;
private final List<CartLineInfo> cartLines = new ArrayList<CartLineInfo>();
public CartInfo() {
}
public int getOrderNum() {
return orderNum;
}
public void setOrderNum(int orderNum) {
this.orderNum = orderNum;
}
public CustomerInfo getCustomerInfo() {
return customerInfo;
}
public void setCustomerInfo(CustomerInfo customerInfo) {
this.customerInfo = customerInfo;
}
public List<CartLineInfo> getCartLines() {
return this.cartLines;
}
private CartLineInfo findLineByCode(String code) {
for (CartLineInfo line : this.cartLines) {
if (line.getProductInfo().getCode().equals(code)) {
return line;
}
}
return null;
}
public void addProduct(ProductInfo productInfo, int quantity) {
CartLineInfo line = this.findLineByCode(productInfo.getCode());
if (line == null) {
line = new CartLineInfo();
line.setQuantity(0);
line.setProductInfo(productInfo);
this.cartLines.add(line);
}
int newQuantity = line.getQuantity() + quantity;
if (newQuantity <= 0) {
this.cartLines.remove(line);
} else {
line.setQuantity(newQuantity);
}
}
public void validate() {
}
public void updateProduct(String code, int quantity) {
CartLineInfo line = this.findLineByCode(code);
if (line != null) {
if (quantity <= 0) {
this.cartLines.remove(line);
} else {
line.setQuantity(quantity);
}
}
}
public void removeProduct(ProductInfo productInfo) {
CartLineInfo line = this.findLineByCode(productInfo.getCode());
if (line != null) {
this.cartLines.remove(line);
}
}
public boolean isEmpty() {
return this.cartLines.isEmpty();
}
public boolean isValidCustomer() {
return this.customerInfo != null && this.customerInfo.isValid();
}
public int getQuantityTotal() {
int quantity = 0;
for (CartLineInfo line : this.cartLines) {
quantity += line.getQuantity();
}
return quantity;
}
public double getAmountTotal() {
double total = 0;
for (CartLineInfo line : this.cartLines) {
total += line.getAmount();
}
return total;
}
public void updateQuantity(CartInfo cartForm) {
if (cartForm != null) {
List<CartLineInfo> lines = cartForm.getCartLines();
for (CartLineInfo line : lines) {
this.updateProduct(line.getProductInfo().getCode(), line.getQuantity());
}
}
}
}
CartLineInfo.java
package org.o7planning.sbshoppingcart.model;
public class CartLineInfo {
private ProductInfo productInfo;
private int quantity;
public CartLineInfo() {
this.quantity = 0;
}
public ProductInfo getProductInfo() {
return productInfo;
}
public void setProductInfo(ProductInfo productInfo) {
this.productInfo = productInfo;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public double getAmount() {
return this.productInfo.getPrice() * this.quantity;
}
}
CustomerInfo.java
package org.o7planning.sbshoppingcart.model;
import org.o7planning.sbshoppingcart.form.CustomerForm;
public class CustomerInfo {
private String name;
private String address;
private String email;
private String phone;
private boolean valid;
public CustomerInfo() {
}
public CustomerInfo(CustomerForm customerForm) {
this.name = customerForm.getName();
this.address = customerForm.getAddress();
this.email = customerForm.getEmail();
this.phone = customerForm.getPhone();
this.valid = customerForm.isValid();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public boolean isValid() {
return valid;
}
public void setValid(boolean valid) {
this.valid = valid;
}
}
OrderDetailInfo.java
package org.o7planning.sbshoppingcart.model;
public class OrderDetailInfo {
private String id;
private String productCode;
private String productName;
private int quanity;
private double price;
private double amount;
public OrderDetailInfo() {
}
// Using for JPA/Hibernate Query.
public OrderDetailInfo(String id, String productCode, //
String productName, int quanity, double price, double amount) {
this.id = id;
this.productCode = productCode;
this.productName = productName;
this.quanity = quanity;
this.price = price;
this.amount = amount;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getProductCode() {
return productCode;
}
public void setProductCode(String productCode) {
this.productCode = productCode;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public int getQuanity() {
return quanity;
}
public void setQuanity(int quanity) {
this.quanity = quanity;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
}
OrderInfo.java
package org.o7planning.sbshoppingcart.model;
import java.util.Date;
import java.util.List;
public class OrderInfo {
private String id;
private Date orderDate;
private int orderNum;
private double amount;
private String customerName;
private String customerAddress;
private String customerEmail;
private String customerPhone;
private List<OrderDetailInfo> details;
public OrderInfo() {
}
// Using for Hibernate Query.
public OrderInfo(String id, Date orderDate, int orderNum, //
double amount, String customerName, String customerAddress, //
String customerEmail, String customerPhone) {
this.id = id;
this.orderDate = orderDate;
this.orderNum = orderNum;
this.amount = amount;
this.customerName = customerName;
this.customerAddress = customerAddress;
this.customerEmail = customerEmail;
this.customerPhone = customerPhone;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Date getOrderDate() {
return orderDate;
}
public void setOrderDate(Date orderDate) {
this.orderDate = orderDate;
}
public int getOrderNum() {
return orderNum;
}
public void setOrderNum(int orderNum) {
this.orderNum = orderNum;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getCustomerAddress() {
return customerAddress;
}
public void setCustomerAddress(String customerAddress) {
this.customerAddress = customerAddress;
}
public String getCustomerEmail() {
return customerEmail;
}
public void setCustomerEmail(String customerEmail) {
this.customerEmail = customerEmail;
}
public String getCustomerPhone() {
return customerPhone;
}
public void setCustomerPhone(String customerPhone) {
this.customerPhone = customerPhone;
}
public List<OrderDetailInfo> getDetails() {
return details;
}
public void setDetails(List<OrderDetailInfo> details) {
this.details = details;
}
}
ProductInfo.java
package org.o7planning.sbshoppingcart.model;
import org.o7planning.sbshoppingcart.entity.Product;
public class ProductInfo {
private String code;
private String name;
private double price;
public ProductInfo() {
}
public ProductInfo(Product product) {
this.code = product.getCode();
this.name = product.getName();
this.price = product.getPrice();
}
// Using in JPA/Hibernate query
public ProductInfo(String code, String name, double price) {
this.code = code;
this.name = name;
this.price = price;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
11. Form bean & Validator
CustomerForm.java
package org.o7planning.sbshoppingcart.form;
import org.o7planning.sbshoppingcart.model.CustomerInfo;
public class CustomerForm {
private String name;
private String address;
private String email;
private String phone;
private boolean valid;
public CustomerForm() {
}
public CustomerForm(CustomerInfo customerInfo) {
if (customerInfo != null) {
this.name = customerInfo.getName();
this.address = customerInfo.getAddress();
this.email = customerInfo.getEmail();
this.phone = customerInfo.getPhone();
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public boolean isValid() {
return valid;
}
public void setValid(boolean valid) {
this.valid = valid;
}
}
ProductForm.java
package org.o7planning.sbshoppingcart.form;
import org.o7planning.sbshoppingcart.entity.Product;
import org.springframework.web.multipart.MultipartFile;
public class ProductForm {
private String code;
private String name;
private double price;
private boolean newProduct = false;
// Upload file.
private MultipartFile fileData;
public ProductForm() {
this.newProduct= true;
}
public ProductForm(Product product) {
this.code = product.getCode();
this.name = product.getName();
this.price = product.getPrice();
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public MultipartFile getFileData() {
return fileData;
}
public void setFileData(MultipartFile fileData) {
this.fileData = fileData;
}
public boolean isNewProduct() {
return newProduct;
}
public void setNewProduct(boolean newProduct) {
this.newProduct = newProduct;
}
}
CustomerFormValidator.java
package org.o7planning.sbshoppingcart.validator;
import org.apache.commons.validator.routines.EmailValidator;
import org.o7planning.sbshoppingcart.form.CustomerForm;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
@Component
public class CustomerFormValidator implements Validator {
private EmailValidator emailValidator = EmailValidator.getInstance();
// This validator only checks for the CustomerForm.
@Override
public boolean supports(Class<?> clazz) {
return clazz == CustomerForm.class;
}
@Override
public void validate(Object target, Errors errors) {
CustomerForm custInfo = (CustomerForm) target;
// Check the fields of CustomerForm.
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "NotEmpty.customerForm.name");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "NotEmpty.customerForm.email");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "address", "NotEmpty.customerForm.address");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "phone", "NotEmpty.customerForm.phone");
if (!emailValidator.isValid(custInfo.getEmail())) {
errors.rejectValue("email", "Pattern.customerForm.email");
}
}
}
ProductFormValidator.java
package org.o7planning.sbshoppingcart.validator;
import org.o7planning.sbshoppingcart.dao.ProductDAO;
import org.o7planning.sbshoppingcart.entity.Product;
import org.o7planning.sbshoppingcart.form.ProductForm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
@Component
public class ProductFormValidator implements Validator {
@Autowired
private ProductDAO productDAO;
// This validator only checks for the ProductForm.
@Override
public boolean supports(Class<?> clazz) {
return clazz == ProductForm.class;
}
@Override
public void validate(Object target, Errors errors) {
ProductForm productForm = (ProductForm) target;
// Check the fields of ProductForm.
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "code", "NotEmpty.productForm.code");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "NotEmpty.productForm.name");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "price", "NotEmpty.productForm.price");
String code = productForm.getCode();
if (code != null && code.length() > 0) {
if (code.matches("\\s+")) {
errors.rejectValue("code", "Pattern.productForm.code");
} else if (productForm.isNewProduct()) {
Product product = productDAO.findProduct(code);
if (product != null) {
errors.rejectValue("code", "Duplicate.productForm.code");
}
}
}
}
}
Файл validation.properties содержит коды оповещения ошибки, когда пользователь вводит в Form объявления продукта и Form информации покупателя.
validation.properties
NotEmpty.customerForm.name=Name is required
NotEmpty.customerForm.email=Email is required
NotEmpty.customerForm.address=Address is required
NotEmpty.customerForm.phone=Phone is required
Pattern.customerForm.email=Email is not valid
NotEmpty.productForm.name=Product name is required
NotEmpty.productForm.code=Product code is required
Pattern.productForm.code=Product code is not valid
Duplicate.productForm.code=Duplicate products
NotFound.loginForm.account=Account not found
Disabled.loginForm.account=Account is disabled
WebConfiguration.java
package org.o7planning.sbshoppingcart.config;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
// Load file: validation.properties
messageSource.setBasename("classpath:validation");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}
12. PaginationResult & Utils
PaginationResult.java
package org.o7planning.sbshoppingcart.pagination;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.query.Query;
public class PaginationResult<E> {
private int totalRecords;
private int currentPage;
private List<E> list;
private int maxResult;
private int totalPages;
private int maxNavigationPage;
private List<Integer> navigationPages;
// @page: 1, 2, ..
public PaginationResult(Query<E> query, int page, int maxResult, int maxNavigationPage) {
final int pageIndex = page - 1 < 0 ? 0 : page - 1;
int fromRecordIndex = pageIndex * maxResult;
int maxRecordIndex = fromRecordIndex + maxResult;
ScrollableResults resultScroll = query.scroll(ScrollMode.SCROLL_INSENSITIVE);
List<E> results = new ArrayList<>();
boolean hasResult = resultScroll.first();
if (hasResult) {
// Scroll to position:
hasResult = resultScroll.scroll(fromRecordIndex);
if (hasResult) {
do {
E record = (E) resultScroll.get(0);
results.add(record);
} while (resultScroll.next()//
&& resultScroll.getRowNumber() >= fromRecordIndex
&& resultScroll.getRowNumber() < maxRecordIndex);
}
// Go to Last record.
resultScroll.last();
}
// Total Records
this.totalRecords = resultScroll.getRowNumber() + 1;
this.currentPage = pageIndex + 1;
this.list = results;
this.maxResult = maxResult;
if (this.totalRecords % this.maxResult == 0) {
this.totalPages = this.totalRecords / this.maxResult;
} else {
this.totalPages = (this.totalRecords / this.maxResult) + 1;
}
this.maxNavigationPage = maxNavigationPage;
if (maxNavigationPage < totalPages) {
this.maxNavigationPage = maxNavigationPage;
}
this.calcNavigationPages();
}
private void calcNavigationPages() {
this.navigationPages = new ArrayList<Integer>();
int current = this.currentPage > this.totalPages ? this.totalPages : this.currentPage;
int begin = current - this.maxNavigationPage / 2;
int end = current + this.maxNavigationPage / 2;
// The first page
navigationPages.add(1);
if (begin > 2) {
// Using for '...'
navigationPages.add(-1);
}
for (int i = begin; i < end; i++) {
if (i > 1 && i < this.totalPages) {
navigationPages.add(i);
}
}
if (end < this.totalPages - 2) {
// Using for '...'
navigationPages.add(-1);
}
// The last page.
navigationPages.add(this.totalPages);
}
public int getTotalPages() {
return totalPages;
}
public int getTotalRecords() {
return totalRecords;
}
public int getCurrentPage() {
return currentPage;
}
public List<E> getList() {
return list;
}
public int getMaxResult() {
return maxResult;
}
public List<Integer> getNavigationPages() {
return navigationPages;
}
}
Utils.java
package org.o7planning.sbshoppingcart.utils;
import javax.servlet.http.HttpServletRequest;
import org.o7planning.sbshoppingcart.model.CartInfo;
public class Utils {
// Products in the cart, stored in Session.
public static CartInfo getCartInSession(HttpServletRequest request) {
CartInfo cartInfo = (CartInfo) request.getSession().getAttribute("myCart");
if (cartInfo == null) {
cartInfo = new CartInfo();
request.getSession().setAttribute("myCart", cartInfo);
}
return cartInfo;
}
public static void removeCartInSession(HttpServletRequest request) {
request.getSession().removeAttribute("myCart");
}
public static void storeLastOrderedCartInSession(HttpServletRequest request, CartInfo cartInfo) {
request.getSession().setAttribute("lastOrderedCart", cartInfo);
}
public static CartInfo getLastOrderedCartInSession(HttpServletRequest request) {
return (CartInfo) request.getSession().getAttribute("lastOrderedCart");
}
}
13. Controllers
MainController.java
package org.o7planning.sbshoppingcart.controller;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.o7planning.sbshoppingcart.dao.OrderDAO;
import org.o7planning.sbshoppingcart.dao.ProductDAO;
import org.o7planning.sbshoppingcart.entity.Product;
import org.o7planning.sbshoppingcart.form.CustomerForm;
import org.o7planning.sbshoppingcart.model.CartInfo;
import org.o7planning.sbshoppingcart.model.CustomerInfo;
import org.o7planning.sbshoppingcart.model.ProductInfo;
import org.o7planning.sbshoppingcart.pagination.PaginationResult;
import org.o7planning.sbshoppingcart.utils.Utils;
import org.o7planning.sbshoppingcart.validator.CustomerFormValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Controller
@Transactional
public class MainController {
@Autowired
private OrderDAO orderDAO;
@Autowired
private ProductDAO productDAO;
@Autowired
private CustomerFormValidator customerFormValidator;
@InitBinder
public void myInitBinder(WebDataBinder dataBinder) {
Object target = dataBinder.getTarget();
if (target == null) {
return;
}
System.out.println("Target=" + target);
// Case update quantity in cart
// (@ModelAttribute("cartForm") @Validated CartInfo cartForm)
if (target.getClass() == CartInfo.class) {
}
// Case save customer information.
// (@ModelAttribute @Validated CustomerInfo customerForm)
else if (target.getClass() == CustomerForm.class) {
dataBinder.setValidator(customerFormValidator);
}
}
@RequestMapping("/403")
public String accessDenied() {
return "/403";
}
@RequestMapping("/")
public String home() {
return "index";
}
// Product List
@RequestMapping({ "/productList" })
public String listProductHandler(Model model, //
@RequestParam(value = "name", defaultValue = "") String likeName,
@RequestParam(value = "page", defaultValue = "1") int page) {
final int maxResult = 5;
final int maxNavigationPage = 10;
PaginationResult<ProductInfo> result = productDAO.queryProducts(page, //
maxResult, maxNavigationPage, likeName);
model.addAttribute("paginationProducts", result);
return "productList";
}
@RequestMapping({ "/buyProduct" })
public String listProductHandler(HttpServletRequest request, Model model, //
@RequestParam(value = "code", defaultValue = "") String code) {
Product product = null;
if (code != null && code.length() > 0) {
product = productDAO.findProduct(code);
}
if (product != null) {
//
CartInfo cartInfo = Utils.getCartInSession(request);
ProductInfo productInfo = new ProductInfo(product);
cartInfo.addProduct(productInfo, 1);
}
return "redirect:/shoppingCart";
}
@RequestMapping({ "/shoppingCartRemoveProduct" })
public String removeProductHandler(HttpServletRequest request, Model model, //
@RequestParam(value = "code", defaultValue = "") String code) {
Product product = null;
if (code != null && code.length() > 0) {
product = productDAO.findProduct(code);
}
if (product != null) {
CartInfo cartInfo = Utils.getCartInSession(request);
ProductInfo productInfo = new ProductInfo(product);
cartInfo.removeProduct(productInfo);
}
return "redirect:/shoppingCart";
}
// POST: Update quantity for product in cart
@RequestMapping(value = { "/shoppingCart" }, method = RequestMethod.POST)
public String shoppingCartUpdateQty(HttpServletRequest request, //
Model model, //
@ModelAttribute("cartForm") CartInfo cartForm) {
CartInfo cartInfo = Utils.getCartInSession(request);
cartInfo.updateQuantity(cartForm);
return "redirect:/shoppingCart";
}
// GET: Show cart.
@RequestMapping(value = { "/shoppingCart" }, method = RequestMethod.GET)
public String shoppingCartHandler(HttpServletRequest request, Model model) {
CartInfo myCart = Utils.getCartInSession(request);
model.addAttribute("cartForm", myCart);
return "shoppingCart";
}
// GET: Enter customer information.
@RequestMapping(value = { "/shoppingCartCustomer" }, method = RequestMethod.GET)
public String shoppingCartCustomerForm(HttpServletRequest request, Model model) {
CartInfo cartInfo = Utils.getCartInSession(request);
if (cartInfo.isEmpty()) {
return "redirect:/shoppingCart";
}
CustomerInfo customerInfo = cartInfo.getCustomerInfo();
CustomerForm customerForm = new CustomerForm(customerInfo);
model.addAttribute("customerForm", customerForm);
return "shoppingCartCustomer";
}
// POST: Save customer information.
@RequestMapping(value = { "/shoppingCartCustomer" }, method = RequestMethod.POST)
public String shoppingCartCustomerSave(HttpServletRequest request, //
Model model, //
@ModelAttribute("customerForm") @Validated CustomerForm customerForm, //
BindingResult result, //
final RedirectAttributes redirectAttributes) {
if (result.hasErrors()) {
customerForm.setValid(false);
// Forward to reenter customer info.
return "shoppingCartCustomer";
}
customerForm.setValid(true);
CartInfo cartInfo = Utils.getCartInSession(request);
CustomerInfo customerInfo = new CustomerInfo(customerForm);
cartInfo.setCustomerInfo(customerInfo);
return "redirect:/shoppingCartConfirmation";
}
// GET: Show information to confirm.
@RequestMapping(value = { "/shoppingCartConfirmation" }, method = RequestMethod.GET)
public String shoppingCartConfirmationReview(HttpServletRequest request, Model model) {
CartInfo cartInfo = Utils.getCartInSession(request);
if (cartInfo == null || cartInfo.isEmpty()) {
return "redirect:/shoppingCart";
} else if (!cartInfo.isValidCustomer()) {
return "redirect:/shoppingCartCustomer";
}
model.addAttribute("myCart", cartInfo);
return "shoppingCartConfirmation";
}
// POST: Submit Cart (Save)
@RequestMapping(value = { "/shoppingCartConfirmation" }, method = RequestMethod.POST)
public String shoppingCartConfirmationSave(HttpServletRequest request, Model model) {
CartInfo cartInfo = Utils.getCartInSession(request);
if (cartInfo.isEmpty()) {
return "redirect:/shoppingCart";
} else if (!cartInfo.isValidCustomer()) {
return "redirect:/shoppingCartCustomer";
}
try {
orderDAO.saveOrder(cartInfo);
} catch (Exception e) {
return "shoppingCartConfirmation";
}
// Remove Cart from Session.
Utils.removeCartInSession(request);
// Store last cart.
Utils.storeLastOrderedCartInSession(request, cartInfo);
return "redirect:/shoppingCartFinalize";
}
@RequestMapping(value = { "/shoppingCartFinalize" }, method = RequestMethod.GET)
public String shoppingCartFinalize(HttpServletRequest request, Model model) {
CartInfo lastOrderedCart = Utils.getLastOrderedCartInSession(request);
if (lastOrderedCart == null) {
return "redirect:/shoppingCart";
}
model.addAttribute("lastOrderedCart", lastOrderedCart);
return "shoppingCartFinalize";
}
@RequestMapping(value = { "/productImage" }, method = RequestMethod.GET)
public void productImage(HttpServletRequest request, HttpServletResponse response, Model model,
@RequestParam("code") String code) throws IOException {
Product product = null;
if (code != null) {
product = this.productDAO.findProduct(code);
}
if (product != null && product.getImage() != null) {
response.setContentType("image/jpeg, image/jpg, image/png, image/gif");
response.getOutputStream().write(product.getImage());
}
response.getOutputStream().close();
}
}
AdminController.java
package org.o7planning.sbshoppingcart.controller;
import java.util.List;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.o7planning.sbshoppingcart.dao.OrderDAO;
import org.o7planning.sbshoppingcart.dao.ProductDAO;
import org.o7planning.sbshoppingcart.entity.Product;
import org.o7planning.sbshoppingcart.form.ProductForm;
import org.o7planning.sbshoppingcart.model.OrderDetailInfo;
import org.o7planning.sbshoppingcart.model.OrderInfo;
import org.o7planning.sbshoppingcart.pagination.PaginationResult;
import org.o7planning.sbshoppingcart.validator.ProductFormValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Controller
@Transactional
public class AdminController {
@Autowired
private OrderDAO orderDAO;
@Autowired
private ProductDAO productDAO;
@Autowired
private ProductFormValidator productFormValidator;
@InitBinder
public void myInitBinder(WebDataBinder dataBinder) {
Object target = dataBinder.getTarget();
if (target == null) {
return;
}
System.out.println("Target=" + target);
if (target.getClass() == ProductForm.class) {
dataBinder.setValidator(productFormValidator);
}
}
// GET: Show Login Page
@RequestMapping(value = { "/admin/login" }, method = RequestMethod.GET)
public String login(Model model) {
return "login";
}
@RequestMapping(value = { "/admin/accountInfo" }, method = RequestMethod.GET)
public String accountInfo(Model model) {
UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
System.out.println(userDetails.getPassword());
System.out.println(userDetails.getUsername());
System.out.println(userDetails.isEnabled());
model.addAttribute("userDetails", userDetails);
return "accountInfo";
}
@RequestMapping(value = { "/admin/orderList" }, method = RequestMethod.GET)
public String orderList(Model model, //
@RequestParam(value = "page", defaultValue = "1") String pageStr) {
int page = 1;
try {
page = Integer.parseInt(pageStr);
} catch (Exception e) {
}
final int MAX_RESULT = 5;
final int MAX_NAVIGATION_PAGE = 10;
PaginationResult<OrderInfo> paginationResult //
= orderDAO.listOrderInfo(page, MAX_RESULT, MAX_NAVIGATION_PAGE);
model.addAttribute("paginationResult", paginationResult);
return "orderList";
}
// GET: Show product.
@RequestMapping(value = { "/admin/product" }, method = RequestMethod.GET)
public String product(Model model, @RequestParam(value = "code", defaultValue = "") String code) {
ProductForm productForm = null;
if (code != null && code.length() > 0) {
Product product = productDAO.findProduct(code);
if (product != null) {
productForm = new ProductForm(product);
}
}
if (productForm == null) {
productForm = new ProductForm();
productForm.setNewProduct(true);
}
model.addAttribute("productForm", productForm);
return "product";
}
// POST: Save product
@RequestMapping(value = { "/admin/product" }, method = RequestMethod.POST)
public String productSave(Model model, //
@ModelAttribute("productForm") @Validated ProductForm productForm, //
BindingResult result, //
final RedirectAttributes redirectAttributes) {
if (result.hasErrors()) {
return "product";
}
try {
productDAO.save(productForm);
} catch (Exception e) {
Throwable rootCause = ExceptionUtils.getRootCause(e);
String message = rootCause.getMessage();
model.addAttribute("errorMessage", message);
// Show product form.
return "product";
}
return "redirect:/productList";
}
@RequestMapping(value = { "/admin/order" }, method = RequestMethod.GET)
public String orderView(Model model, @RequestParam("orderId") String orderId) {
OrderInfo orderInfo = null;
if (orderId != null) {
orderInfo = this.orderDAO.getOrderInfo(orderId);
}
if (orderInfo == null) {
return "redirect:/admin/orderList";
}
List<OrderDetailInfo> details = this.orderDAO.listOrderDetailInfos(orderId);
orderInfo.setDetails(details);
model.addAttribute("orderInfo", orderInfo);
return "order";
}
}
14. Static & Thymeleaf Templates
styles.css
html {
background: white;
}
h3 {
margin: 0px;
padding: 0px;
}
body {
max-width: 860px;
min-width: 360px;
margin: 0px auto;
background: #F8F8F8;
padding:0px 5px;
text-align:center;
}
.page-title {
font-size:120%;
text-align: left;
margin:10px 0px;
}
.header-container {
text-align: left;
border-bottom: 1px solid #ccc;
position: relative;
background: #5f5f5f;
color: white;
}
.header-container .header-bar {
position: absolute;
right: 10px;
bottom: 20px;
}
.header-container .header-bar a {
color: white;
font-size: bold;
}
.footer-container {
text-align: center;
margin-top: 10px;
padding: 5px 0px 0px 0px;
border-top: 1px solid #ccc;
}
.menu-container {
text-align: right;
margin: 10px 5px;
}
.menu-container a {
margin: 5px 5px 5px 10px;
color: #004d99;
font-weight: bold;
text-decoration: none;
}
.site-name {
font-size:200%;
margin:10px 10px 10px 0px;
padding: 5px;
}
.container {
margin: 5px 0px;
}
.demo-container, .login-container, .account-container {
padding: 5px;
border: 1px solid #ccc;
text-align:left;
margin:20px 0px;
}
.customer-info-container {
text-align: left;
border: 1px solid #ccc;
padding: 0px 5px;
}
.product-preview-container {
border: 1px solid #ccc;
padding: 5px;
width: 250px;
margin: 10px ;
display: inline-block;
text-align:left;
}
.product-preview-container input {
width: 50px;
}
.product-image {
width: 120px;
height: 80px;
}
ul {
list-style-type: none;
padding-left: 5px;
margin:5px;
}
.navi-item {
margin: 5px 5px 5px 20px;
}
.button-update-sc {
color: red;
margin: 5px 5px 5px 20px;
}
.button-send-sc {
color: red;
margin: 5px 5px 5px 20px;
}
.error-message {
font-size: 90%;
color: red;
font-style: italic;
}
.price {
color: blue;
font-weight: bold;
}
.subtotal {
color: red;
font-weight: bold;
}
.total {
color: red;
font-weight: bold;
font-size: 120%;
}
table td {
padding: 5px;
}
_footer.html
<div class="footer-container" xmlns:th="http://www.thymeleaf.org">
@Copy right <a href="http://o7planning.org" target="_blank">o7planning.org</a>
<br>
See more <a>demo</a>
</div>
_header.html
<div class="header-container"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<div class="site-name">Online Shop</div>
<div class="header-bar">
<th:block sec:authorize="isAuthenticated()">
Hello
<a th:href="@{/admin/accountInfo}" th:utext="${#request.userPrincipal.name}">..</a>
|
<a th:href="@{/admin/logout}">Logout</a>
</th:block>
<th:block sec:authorize="!isAuthenticated()">
<a th:href="@{/admin/login}">Login</a>
</th:block>
</div>
</div>
_menu.html
<div class="menu-container"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4" >
<a th:href="@{/}">Home</a>
|
<a th:href="@{/productList}">
Product List
</a>
|
<a th:href="@{/shoppingCart}">
My Cart
</a>
|
<th:block sec:authorize="hasAnyRole('ROLE_MANAGER','ROLE_EMPLOYEE')">
<a th:href="@{/admin/orderList}">
Order List
</a>
|
</th:block>
<th:block sec:authorize="hasRole('ROLE_MANAGER')">
<a th:href="@{/admin/product}">
Create Product
</a>
|
</th:block>
</div>
403.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Access Denied</title>
<link rel="stylesheet" type="text/css" th:href="@{/styles.css}">
</head>
<body>
<th:block th:include="/_header"></th:block>
<th:block th:include="/_menu"></th:block>
<div class="page-title">Access Denied!</div>
<h3 style="color:red;">Sorry, you can not access this page!</h3>
<th:block th:include="/_footer"></th:block>
</body>
</html>
accountInfo.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Account Info</title>
<link rel="stylesheet" type="text/css" th:href="@{/styles.css}">
</head>
<body>
<th:block th:include="/_header"></th:block>
<th:block th:include="/_menu"></th:block>
<div class="page-title">Account Info</div>
<div class="account-container">
<ul>
<li>User Name: <span th:utext="${#request.userPrincipal.name}"></span></li>
<li>
Role:
<ul>
<li th:each="auth : ${userDetails.authorities}" th:utext="${auth.authority}"></li>
</ul>
</li>
</ul>
</div>
<th:block th:include="/_footer"></th:block>
</body>
</html>
index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Books Shop Online</title>
<link rel="stylesheet" type="text/css" th:href="@{/styles.css}" />
</head>
<body>
<th:block th:include="/_header"></th:block>
<th:block th:include="/_menu"></th:block>
<div class="page-title">Shopping Cart Demo</div>
<div class="demo-container">
<h3>Demo content</h3>
<ul>
<li>Buy online</li>
<li>Admin pages</li>
<li>Reports</li>
</ul>
</div>
<th:block th:include="/_footer"></th:block>
</body>
</html>
login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Login</title>
<link rel="stylesheet" type="text/css" th:href="@{/styles.css}">
</head>
<body>
<th:block th:include="/_header"></th:block>
<th:block th:include="/_menu"></th:block>
<div class="page-title">Login (For Employee, Manager)</div>
<div class="login-container">
<h3>Enter username and password</h3>
<br>
<!-- /login?error=true -->
<th:block th:if="${#session != null && #session.getAttribute('SPRING_SECURITY_LAST_EXCEPTION') != null}">
<div th:if= "${#request.getParameter('error') == 'true'}"
style="color: red; margin: 10px 0px;">
Login Failed!!!<br /> Reason :
<span th:utext="${#session.getAttribute('SPRING_SECURITY_LAST_EXCEPTION').message}"></span>
</div>
</th:block>
<form method="POST"
th:action="@{/j_spring_security_check}">
<table>
<tr>
<td>User Name *</td>
<td><input name="userName" /></td>
</tr>
<tr>
<td>Password *</td>
<td><input type="password" name="password" /></td>
</tr>
<tr>
<td> </td>
<td>
<input type="submit" value="Login" />
<input type="reset" value="Reset" />
</td>
</tr>
</table>
</form>
<span class="error-message" th:utext="${error}"></span>
</div>
<div style="text-align:left">
<h3>User/Password:</h3>
<ul>
<li>employee1/123</li>
<li>manager1/123</li>
</ul>
</div>
<th:block th:include="/_footer"></th:block>
</body>
</html>
order.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Product List</title>
<link rel="stylesheet" type="text/css" th:href="@{/styles.css}">
</head>
<body>
<th:block th:include="/_header"></th:block>
<th:block th:include="/_menu"></th:block>
<div class="page-title">Order Info</div>
<div class="customer-info-container">
<h3>Customer Information:</h3>
<ul>
<li>Name: <span th:utext="${orderInfo.customerName}"></span></li>
<li>Email: <span th:utext="${orderInfo.customerEmail}"></span></li>
<li>Phone: <span th:utext="${orderInfo.customerPhone}"></span></li>
<li>Address: <span th:utext="${orderInfo.customerAddress}"></span></li>
</ul>
<h3>Order Summary:</h3>
<ul>
<li>Total:
<span class="total" th:utext="${#numbers.formatDecimal(orderInfo.amount,3,2,'COMMA')}">
</span>
</li>
</ul>
</div>
<br/>
<table border="1" style="width:100%">
<tr>
<th>Product Code</th>
<th>Product Name</th>
<th>Quantity</th>
<th>Price</th>
<th>Amount</th>
</tr>
<tr th:each="orderDetailInfo : ${orderInfo.details}">
<td th:utext="${orderDetailInfo.productCode}"></td>
<td th:utext="${orderDetailInfo.productName}"></td>
<td th:utext="${orderDetailInfo.quanity}"></td>
<td th:utext="${#numbers.formatDecimal(orderDetailInfo.price,3,2,'COMMA')}"></td>
<td th:utext="${#numbers.formatDecimal(orderDetailInfo.amount,3,2,'COMMA')}"></td>
</tr>
</table>
<th:block th:include="/_footer"></th:block>
</body>
</html>
orderList.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Product List</title>
<link rel="stylesheet" type="text/css" th:href="@{/styles.css}">
</head>
<body>
<th:block th:include="/_header"></th:block>
<th:block th:include="/_menu"></th:block>
<div class="page-title">Order List</div>
<div>Total Order Count: <span th:utext="${paginationResult.totalRecords}"></span></div>
<table border="1" style="width:100%">
<tr>
<th>Order Num</th>
<th>Order Date</th>
<th>Customer Name</th>
<th>Customer Address</th>
<th>Customer Email</th>
<th>Amount</th>
<th>View</th>
</tr>
<tr th:each="orderInfo : ${paginationResult.list}">
<td th:utext="${orderInfo.orderNum}"></td>
<td th:utext="${#dates.format(orderInfo.orderDate,'dd-MM-yyyy HH:mm')}"></td>
<td th:utext="${orderInfo.customerName}"></td>
<td th:utext="${orderInfo.customerAddress}"></td>
<td th:utext="${orderInfo.customerEmail}"></td>
<td style="color:red;" th:utext="${#numbers.formatDecimal(orderInfo.amount,3,2,'COMMA')}">
</td>
<td><a th:href="@{|/admin/order?orderId=${orderInfo.id}|}">View</a></td>
</tr>
</table>
<div class="page-navigator" th:if="${paginationResult.totalPages > 1}" >
<th:block th:each="page: ${paginationResult.navigationPages}">
<a th:if="${page != -1}" class="nav-item"
th:href="@{|/admin/orderList?page=${page}|}" th:utext="${page}"></a>
<span th:if="${page == -1}" class="nav-item"> ... </span>
</th:block>
</div>
<th:block th:include="/_footer"></th:block>
</body>
</html>
product.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Product</title>
<link rel="stylesheet" type="text/css" th:href="@{/styles.css}">
</head>
<body>
<th:block th:include="/_header"></th:block>
<th:block th:include="/_menu"></th:block>
<div class="page-title">Product</div>
<div th:if="${errorMessage!= null}" class="error-message" th:utext="${errorMessage}">
</div>
<form th:object="${productForm}" th:action="@{/admin/product}"
method="POST" enctype="multipart/form-data">
<table style="text-align:left;">
<tr>
<td>Code *</td>
<td style="color:red;">
<input th:field="*{newProduct}" type="hidden" />
<input th:if="${productForm.newProduct}" type="text"
th:field="*{code}" />
<th:block th:if="${!productForm.newProduct}">
<span th:utext="${productForm.code}"></span>
<input type="hidden" th:field="*{code}" />
</th:block>
</td>
<td>
<span class="error-message" th:if="${#fields.hasErrors('code')}" th:errors="*{code}">..</span>
</td>
</tr>
<tr>
<td>Name *</td>
<td><input th:field="*{name}" /></td>
<td>
<span class="error-message" th:if="${#fields.hasErrors('name')}" th:errors="*{name}">..</span>
</td>
</tr>
<tr>
<td>Price *</td>
<td><input th:field="*{price}" /></td>
<td>
<span class="error-message" th:if="${#fields.hasErrors('price')}" th:errors="*{price}">..</span>
</td>
</tr>
<tr>
<td>Image</td>
<td>
<img th:src="@{|/productImage?code=${productForm.code}|}" width="100"/>
</td>
<td> </td>
</tr>
<tr>
<td>Upload Image</td>
<td><input type="file" th:field="*{fileData}" /></td>
<td> </td>
</tr>
<tr>
<td> </td>
<td>
<input type="submit" value="Submit" />
<input type="reset" value="Reset" />
</td>
</tr>
</table>
</form>
<th:block th:include="/_footer"></th:block>
</body>
</html>
productList.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8">
<title>Product List</title>
<link rel="stylesheet" type="text/css" th:href="@{/styles.css}">
</head>
<body>
<th:block th:include="/_header"></th:block>
<th:block th:include="/_menu"></th:block>
<div class="page-title">Product List</div>
<div class="product-preview-container" th:each="prodInfo : ${paginationProducts.list}">
<ul>
<li><img class="product-image"
th:src="@{|/productImage?code=${prodInfo.code}|}" /></li>
<li>Code: <span th:utext="${prodInfo.code}"></span></li>
<li>Name: <span th:utext="${prodInfo.name}"></span></li>
<li>Price: <span th:utext="${#numbers.formatDecimal(prodInfo.price,3,2,'COMMA')}"></span></li>
<li>
<a th:href="@{|/buyProduct?code=${prodInfo.code}|}">Buy Now</a>
</li>
<!-- For Manager edit Product -->
<th:block sec:authorize="hasAuthority('ROLE_MANAGER')">
<li>
<a style="color:red;"
th:href="@{|/admin/product?code=${prodInfo.code}|}">Edit Product</a>
</li>
</th:block>
</ul>
</div>
<br/>
<div class="page-navigator" th:if="${paginationProducts.totalPages > 1}">
<th:block th:each="page : ${paginationProducts.navigationPages}">
<a th:href="@{|/productList?page=${page}|}" th:if="${page != -1}"
class="nav-item" th:utext="${page}"></a>
<span class="nav-item" th:if="${page == -1}"> ... </span>
</th:block>
</div>
<th:block th:include="/_footer"></th:block>
</body>
</html>
shoppingCart.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Shopping Cart</title>
<link rel="stylesheet" type="text/css" th:href="@{/styles.css}">
</head>
<body>
<th:block th:include="/_header"></th:block>
<th:block th:include="/_menu"></th:block>
<div class="page-title">My Cart</div>
<th:block th:if="${cartForm == null || cartForm.cartLines == null || cartForm.cartLines.empty}">
<h2>There is no items in Cart</h2>
<a th:href="@{/productList}">Show Product List</a>
</th:block>
<th:block th:if="${cartForm != null && cartForm.cartLines != null && !cartForm.cartLines.empty}">
<form method="POST" th:object="${cartForm}" th:action="@{/shoppingCart}">
<div class="product-preview-container"
th:each="cartLineInfo, varStatus : ${cartForm.cartLines}">
<ul>
<li><img class="product-image"
th:src="@{|/productImage?code=${cartLineInfo.productInfo.code}|}" />
</li>
<li>Code: <span th:utext="${cartLineInfo.productInfo.code}"></span>
<input type="hidden"
th:name="|cartLines[${varStatus.index}].productInfo.code|"
th:value="${cartLineInfo.productInfo.code}" />
</li>
<li>Name: <span th:utext="${cartLineInfo.productInfo.name}"></span></li>
<li>Price:
<span class="price"
th:utext="${#numbers.formatDecimal(cartLineInfo.productInfo.price,3,2,'COMMA')}">
</span>
</li>
<li>Quantity:
<input
th:name="|cartLines[${varStatus.index}].quantity|"
th:value="${cartLineInfo.quantity}" />
</li>
<li>Subtotal:
<span class="subtotal"
th:utext="${#numbers.formatDecimal(cartLineInfo.amount,3,2,'COMMA')}">
</span>
</li>
<li>
<a th:href="@{|/shoppingCartRemoveProduct?code=${cartLineInfo.productInfo.code}|}">
Delete
</a>
</li>
</ul>
</div>
<div style="clear: both"></div>
<input class="button-update-sc" type="submit" value="Update Quantity" />
<a class="navi-item"
th:href="@{/shoppingCartCustomer}">Enter Customer Info</a>
<a class="navi-item"
th:href="@{/productList}">Continue Buy</a>
</form>
</th:block>
<th:block th:include="/_footer"></th:block>
</body>
</html>
shoppingCartConfirmation.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Shopping Cart Confirmation</title>
<link rel="stylesheet" type="text/css" th:href="@{/styles.css}">
</head>
<body>
<th:block th:include="/_header"></th:block>
<th:block th:include="/_menu"></th:block>
<div class="page-title">Confirmation</div>
<div class="customer-info-container">
<h3>Customer Information:</h3>
<ul>
<li>Name: <span th:utext="${myCart.customerInfo.name}"></span></li>
<li>Email: <span th:utext="${myCart.customerInfo.email}"></span></li>
<li>Phone: <span th:utext="${myCart.customerInfo.phone}"></span></li>
<li>Address: <span th:utext="${myCart.customerInfo.address}"></span></li>
</ul>
<h3>Cart Summary:</h3>
<ul>
<li>Quantity: <span th:utext="${myCart.quantityTotal}"></span>$</li>
<li>Total:
<span class="total"
th:utext="${#numbers.formatDecimal(myCart.amountTotal,3,2,'COMMA')}">
</span>
</li>
</ul>
</div>
<form method="POST" th:action="@{/shoppingCartConfirmation}">
<!-- Edit Cart -->
<a class="navi-item" th:href="@{/shoppingCart}">
Edit Cart
</a>
<!-- Edit Customer Info -->
<a class="navi-item" th:href="@{/shoppingCartCustomer}">
Edit Customer Info
</a>
<!-- Send/Save -->
<input type="submit" value="Send" class="button-send-sc" />
</form>
<div class="container">
<div class="product-preview-container" th:each="cartLineInfo : ${myCart.cartLines}">
<ul>
<li>
<img class="product-image"
src="@{|/productImage?code=${cartLineInfo.productInfo.code}|}" />
</li>
<li>
Code: <span th:utext="${cartLineInfo.productInfo.code}"></span>
<input
type="hidden" name="code" th:value="${cartLineInfo.productInfo.code}" />
</li>
<li>Name: <span th:utext="${cartLineInfo.productInfo.name}"></span></li>
<li>Price:
<span class="price"
th:utext="${#numbers.formatDecimal(cartLineInfo.productInfo.price,3,2,'COMMA')}">
</span>
</li>
<li>Quantity: <span th:utext="${cartLineInfo.quantity}"></span></li>
<li>Subtotal:
<span class="subtotal"
th:utext="${#numbers.formatDecimal(cartLineInfo.amount,3,2,'COMMA')}">
</span>
</li>
</ul>
</div>
</div>
<th:block th:include="/_footer"></th:block>
</body>
</html>
shoppingCartCustomer.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Enter Customer Information</title>
<link rel="stylesheet" type="text/css" th:href="@{/styles.css}">
</head>
<body>
<th:block th:include="/_header"></th:block>
<th:block th:include="/_menu"></th:block>
<div class="page-title">Enter Customer Information</div>
<form method="POST" th:object = "${customerForm}" th:action="@{/shoppingCartCustomer}">
<table>
<tr>
<td>Name *</td>
<td><input th:field="*{name}" /></td>
<td>
<span class="error-message"
th:if="${#fields.hasErrors('name')}" th:errors="*{name}">..</span>
</td>
</tr>
<tr>
<td>Email *</td>
<td><input th:field="*{email}" /></td>
<td>
<span class="error-message"
th:if="${#fields.hasErrors('email')}" th:errors="*{email}">..</span>
</td>
</tr>
<tr>
<td>Phone *</td>
<td><input th:field="*{phone}" /></td>
<td>
<span class="error-message"
th:if="${#fields.hasErrors('phone')}" th:errors="*{phone}">..</span>
</td>
</tr>
<tr>
<td>Address *</td>
<td><input th:field="*{address}" /></td>
<td>
<span class="error-message"
th:if="${#fields.hasErrors('address')}" th:errors="*{address}">..</span>
</td>
</tr>
<tr>
<td> </td>
<td><input type="submit" value="Submit" /> <input type="reset"
value="Reset" /></td>
</tr>
</table>
</form>
<th:block th:include="/_footer"></th:block>
</body>
</html>
shoppingCartFinalize.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Shopping Cart Finalize</title>
<link rel="stylesheet" type="text/css" th:href="@{/styles.css}">
</head>
<body>
<th:block th:include="/_header"></th:block>
<th:block th:include="/_menu"></th:block>
<div class="page-title">Finalize</div>
<div class="container">
<h3>Thank you for Order</h3>
Your order number is: <span th:utext="${lastOrderedCart.orderNum}"></span>
</div>
<th:block th:include="/_footer"></th:block>
</body>
</html>
Руководства Spring Boot
- Установите Spring Tool Suite для Eclipse
- Руководство Spring для начинающих
- Руководство Spring Boot для начинающих
- Общие свойства Spring Boot
- Руководство Spring Boot и Thymeleaf
- Руководство Spring Boot и FreeMarker
- Руководство Spring Boot и Groovy
- Руководство Spring Boot и Mustache
- Руководство Spring Boot и JSP
- Руководство Spring Boot, Apache Tiles, JSP
- Используйте Logging в Spring Boot
- Мониторинг приложений с помощью Spring Boot Actuator
- Создание веб-приложения с несколькими языками с помощью Spring Boot
- Используйте несколько ViewResolver в Spring Boot
- Используйте Twitter Bootstrap в Spring Boot
- Руководство Spring Boot Interceptor
- Руководство Spring Boot, Spring JDBC и Spring Transaction
- Руководство Spring JDBC
- Руководство Spring Boot, JPA и Spring Transaction
- Руководство Spring Boot и Spring Data JPA
- Руководство Spring Boot, Hibernate и Spring Transaction
- Интеграция Spring Boot, JPA и H2 Database
- Руководство Spring Boot и MongoDB
- Используйте несколько DataSources с Spring Boot и JPA
- Используйте несколько DataSource с Spring Boot и RoutingDataSource
- Создайте приложение для входа с Spring Boot, Spring Security, Spring JDBC
- Создайте приложение для входа с Spring Boot, Spring Security, JPA
- Создайте приложение регистрации пользователей с помощью Spring Boot, Spring Form Validation
- Пример OAuth2 Social Login в Spring Boot.
- Запускать фоновые запланированные задачи в Spring
- Пример CRUD Restful Web Service c Spring Boot
- Пример Spring Boot Restful Client c RestTemplate
- Пример CRUD с Spring Boot, REST и AngularJS
- Защита Spring Boot RESTful Service используя Basic Authentication
- Защита Spring Boot RESTful Service используя Auth0 JWT
- Пример Upload file c Spring Boot
- Пример Download file c Spring Boot
- Пример Upload file c Spring Boot и jQuery Ajax
- Пример Upload file c Spring Boot и AngularJS
- Создание веб-приложения для корзины покупок с помощью Spring Boot, Hibernate
- Руководство Spring Email
- Создайте простое приложение Chat с Spring Boot и Websocket
- Разверните приложение Spring Boot на Tomcat Server
- Развертывание приложения Spring Boot на Oracle WebLogic Server
- Установите бесплатный сертификат Let's Encrypt SSL для Spring Boot
- Настройте Spring Boot для перенаправления HTTP на HTTPS
Show More