SelimSql

Database Management System

Main Page User Guide Download Java Code Android Contact

Spring Boot + Hibernate JPA + REST CRUD Example

By Zülkif Güven | June 10, 2018
In this tutorial, Spring Boot, Hibernate, JPA and REST techniques are explained.

Brief Description of Terminology

ORM(Object-Relational Mapping) is a process of mapping java objects and database tables.

JPA(Java Persistence API) is one of the possible approaches to ORM. JPA has several popular implementations like Hibernate, Apache OpenJPA and EclipseLink. Persistence metadata is used to map between java objects and database tables. JPA typically defines the metadata via annotations in the Java class. Alternatively the metadata can be defined via XML.
In this example annotations are used.

Hibernate is an ORM solution for Java. It is an open source persistent framework. It is a high performance Object-Relational persistence and Query service for Java applications. Hibernate maps database tables to java classes. It uses various Java APIs, like JDBC, Java Transaction API(JTA) and Java Naming and Directory Interface (JNDI).

REST(Representational State Transfer) is an architectural style for designing web services. REST or RESTful web services provide a communication environment between software applications on the Internet. REST is not the same as HTTP, but it is mostly associated with it.
Main Features of REST:
JavaScript Object Notation (JSON) is a lightweight syntax for data exchange.
It is designed to be easily understood by humans and easly parsed by machines. JSON is mostly used for transmitting data through REST web services.
Example JSON Data:
{"id":1, "name":"Selim", "age":12, "member":false}
JDBC is acronym of Java Database Connectivity.

CRUD is acronym of Create, Retrieve, Update, Delete.


Used Technologies


1. Project Structure

This sample spring boot web application explains the CRUD operations on USER table by REST methods.




2. Building SelimSQL Database

Build a new SSql database and user table by executing the following script.
For executing script, you can download and install this tool: SelimSQL Database Developer

a) Build a new SSql database using SSqlDeveloper tool:

JDBC Class: selimsql.jdbc.DbDriver
JDBC.Url  : jdbc:selimsql:fileNewDatabase:/MyData/RestTest

Press the "Connect" button.
Enter the user and password of you.

(jdbc.username): admin
(jdbc.password) may be empty!

b) Build a new table and related indexes by executing the following 4 scripts one by one:

Create Table User (
  Id BIGINT NOT NULL,
  Code VARCHAR(30) NOT NULL,
  Name VARCHAR(30) NOT NULL,
  Surname VARCHAR(30) NOT NULL,
  Password VARCHAR(100) NOT NULL,
  Phone VARCHAR(20),
  Status SmallInt NOT NULL
);
--Note: Select the script above and press "Sql Execute" button.

Create Unique Index UserPK On User(Id);
Create Unique Index UserCodeUIdx On User(Code);
  
Create SEQUENCE SEQ_USER_PK;

c) Insert a User row example(optional):

Insert into User(Id, Code, Name, Surname, Password, Phone, Status)
Values(SequenceNextValue('SEQ_USER_PK'), 'admin', 'Admin', 'Admin', '1111', null, 1);

d) After the execution of script;

check "src\main\resources\spring\application.properties" named config file in project as follow:
jdbc.driverClassName=selimsql.jdbc.DbDriver

#Database jdbc connection url that you have just built:
jdbc.url=jdbc:selimsql:file:/MyData/RestTest
jdbc.username=admin

#Database user password that you have entered:
jdbc.password=[may be empty]


3. Project Maven Dependency

Add spring boot and necessary database dependencies in Maven pom.xml file:
<?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>com.selimsql.springboot</groupId>
  <artifactId>SpBootRest</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <name>SPBootRestApp</name>
  <description>SP Boot Rest App</description>

  <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>1.5.8.RELEASE</version>
      <relativePath/>
  </parent>

  <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      <java.version>1.8</java.version>

      <selimSqlDb.version>1.3.0</selimSqlDb.version>
  </properties>

  <dependencies>
    <!-- For Web Application -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- For Hibernate JPA -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- SelimSql database driver; Note need "repositories" below -->
    <dependency>
      <groupId>selimSql</groupId>
      <artifactId>selimSql</artifactId>
      <version>${selimSqlDb.version}</version>
    </dependency>
  </dependencies>

  <!-- For loading SelimSql database driver -->
  <repositories>
    <repository>
      <id>com.selimsql.database.driver.release</id>
      <name>SelimSql Database Driver Repository</name>
      <url>http://www.selimsql.com/release</url>
    </repository>
  </repositories>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
          <mainClass>com.selimsql.springboot.AppStarter</mainClass>
        </configuration>

        <executions>
          <execution>
            <goals>
            <goal>repackage</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>


4. Table Row Data Model

Add a User named persistence class to implement user’s data:
package com.selimsql.springboot.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "User")
public class User implements java.io.Serializable {
  private static final long serialVersionUID = 1L;

  @Id
  @Column(name="Id", unique=true, nullable=false)
  private Long id;  //uses SEQ_USER_PK to get new Id!

  @Column(name="Code", unique=true, nullable=false, length=30)
  private String code;

  @Column(name="Name", nullable=false, length=30)
  private String name;

  @Column(name="Surname", nullable=false, length=30)
  private String surname;

  @Column(name="Password", nullable=false, length=100)
  private String password;

  @Column(name="Phone", length=20)
  private String phone;

  @Column(name="Status", nullable=false)
  private Integer status;


  public User() {
    this.id = null;
    this.code = null;
    //..
  }

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }


  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 String getSurname() {
    return surname;
  }

  public void setSurname(String surname) {
    this.surname = surname;
  }

  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  public String getPhone() {
    return phone;
  }

  public void setPhone(String phone) {
    this.phone = phone;
  }

  public Integer getStatus() {
    return status;
  }

  public void setStatus(Integer status) {
    this.status = status;
  }


  @Override
  public String toString() {
    return "User.id:" + id + ", code:" + code
        + ", name:" + name+ ", surname:" + surname
        + ", status:" + status;
    //..
  }
}


5. Data Access Object(DAO) Layer

User DAO interface class:
package com.selimsql.springboot.dao;

import java.util.List;

import com.selimsql.springboot.model.User;

public interface UserDAO {

  User findOne(Long id);

  List<User> findAll();

  User insert(User user);

  User update(User user);

  void delete(User user);
}


User Dao implementation class for Hibernate JPA operations:
package com.selimsql.springboot.dao;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;

import org.springframework.stereotype.Repository;

import com.selimsql.springboot.model.User;
import com.selimsql.springboot.util.Util;


@Repository
public class UserDAOImpl implements UserDAO {

  private String sequenceNamePk;

  private EntityManager entityManager;

  public UserDAOImpl() {
    this.sequenceNamePk = "SEQ_USER_PK";
  }

  @PersistenceContext
  private void setEntityManager(EntityManager entityManager) {
    this.entityManager = entityManager;
  }


  @Override
  public User findOne(Long id) {
    System.out.println("findOne.keyId:" + id);

    if (id==null)
      return null;

    User row = entityManager.find(User.class, id);
    return row;
  }


  @Override
  public List<User> findAll() {
    System.out.println("findAll");

    try {
      String sql = "Select t From User t Order by t.id";
      TypedQuery<User> query = entityManager.createQuery(sql, User.class);

      List<User> list = query.getResultList();

      return list;
    }
    catch (Throwable ext) {
      throw ext;
    }
  }

  
  @Override
  public User insert(User user) {
    System.out.println("insert row: " + user);

    Long idSeq = user.getId();
    if (idSeq==null) {
      idSeq = getNextIdFromSequence();
      user.setId(idSeq);
    }

    entityManager.persist(user);

    return user;
  }


  public Long getNextIdFromSequence() {
    return getNextIdFromSequenceSSql(sequenceNamePk);
  }


  private Long getNextIdFromSequenceSSql(String sequenceName) {
    String sql = "Select SequenceNextValue(SequenceName)"
          + "  From _DB_Sequence Where SequenceName = :pSeqName";
    return getNextIdFromSequence(sql, sequenceName);
  }

  private Long getNextIdFromSequence(String sql, String sequenceName) {
    Object objNextId;
    try {
      Query query = entityManager.createNativeQuery(sql);
      query.setParameter("pSeqName", sequenceName);
      objNextId = query.getSingleResult();
    }
    catch (NoResultException norex) {
      objNextId = null;
    }

    return Util.getLongObj(objNextId);
  }


  @Override
  public User update(User user) {
    System.out.println("update row: " + user);
    User userUpdated = entityManager.merge(user);
    return userUpdated;
  }

  @Override
  public void delete(User user) { 
    System.out.println("delete row: " + user);
    entityManager.remove(user);
  }
}


6. Service Layer

User Service Implementation:
package com.selimsql.springboot.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.selimsql.springboot.dao.UserDAO;
import com.selimsql.springboot.model.User;

@Service
@Transactional(readOnly = true, isolation = Isolation.DEFAULT, propagation = Propagation.SUPPORTS, rollbackFor = Throwable.class)
public class UserServiceImpl implements UserService {

  private UserDAO userDAO;

  @Autowired
  public UserServiceImpl(UserDAO userDAO) {
    this.userDAO = userDAO;
  }

  @Override
  @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
  public User findOne(Long id) {
    User user = userDAO.findOne(id);
    return user;
  }

  @Override
  @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
  public List<User> findAll() {
    List<User> list = userDAO.findAll();
    return list;
  }

  @Override
  @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
  public User insert(User user) {
    User userInserted = userDAO.insert(user);
    return userInserted;
  }

  @Override
  @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
  public User update(User user) {
    User userUpdated = userDAO.update(user);
    return userUpdated;
  }

  @Override
  @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
  public void delete(User user) {
    userDAO.delete(user);
  }
}


7. REST Controller Layer

User REST controller service:
package com.selimsql.springboot.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.selimsql.springboot.dto.CustomResponseDTO;
import com.selimsql.springboot.exception.BusinessHttpStatusRuntimeException;
import com.selimsql.springboot.model.User;
import com.selimsql.springboot.service.UserService;
import com.selimsql.springboot.util.Util;

@RestController
@RequestMapping("/api/user")
public class UserRestController {

  private UserService userService;

  @Autowired
  public UserRestController(UserService userService) {
    this.userService = userService;
  }

  @RequestMapping(path = "/userList",
      method = RequestMethod.GET,
      produces = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity<List<User>> userList() {
    System.out.println("userList");

    List<User> list = userService.findAll();
    ResponseEntity<List<User>> resp = new ResponseEntity<>(list, HttpStatus.OK);
    return resp;
  }

  @RequestMapping(path = "/userRow/{userId}",
      method = RequestMethod.GET,
      produces = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity<User> userRow(@PathVariable Long userId) {
    System.out.println("userRow.id:" + userId);

    User user = userService.findOne(userId);
    ResponseEntity<User> resp = new ResponseEntity<>(user, HttpStatus.OK);
    return resp;
  }

  @RequestMapping(path = "/add",
      method = RequestMethod.POST,
      consumes = MediaType.APPLICATION_JSON_VALUE,
      produces = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity<CustomResponseDTO> addUser(@RequestBody User userNew) {
    String userCode = (userNew == null ? null : userNew.getCode());
    System.out.println("addUser.userCode:" + userCode);

    User userInserted = userService.insert(userNew);

    String message = "User registration is successful";
    ResponseEntity<CustomResponseDTO> responseEntity = newResponseEntityByCustomResponseDTO(userInserted.getId(), message, HttpStatus.CREATED);
    return responseEntity;
  }

  @RequestMapping(path = "/update/{userId}",
      method = RequestMethod.PUT,
      consumes = MediaType.APPLICATION_JSON_VALUE,
      produces = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity<User> updateUser(@PathVariable Long userId, @RequestBody User userUpdate) {
    System.out.println("updateUser.id" + userId + ", row:" + userUpdate);

    User userUpdated = userService.update(userUpdate);
    ResponseEntity<User> responseEntity = new ResponseEntity<>(userUpdated, HttpStatus.OK);
    return responseEntity;
  }

  @RequestMapping(path = "/delete/{userId}",
      method = RequestMethod.DELETE,
      consumes = MediaType.APPLICATION_JSON_VALUE,
      produces = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity<CustomResponseDTO> deleteUserSave(@PathVariable Long userId) {
    System.out.println("deleteUser.id:" + userId);

    User userDb = userService.findOne(userId);
    if (userDb == null) {
      //throw new BusinessHttpStatusRuntimeException(HttpStatus.BAD_REQUEST, "RowId:" + userId + " invalid!");
      ResponseEntity<CustomResponseDTO> responseEntity = newResponseEntityByCustomResponseDTO(userId, "RowId:" + userId + " absent!", HttpStatus.NOT_FOUND);
      return responseEntity;
    }

    userService.delete(userDb);

    String message = "User deletion is successful";
    ResponseEntity<CustomResponseDTO> responseEntity = newResponseEntityByCustomResponseDTO(userId, message, HttpStatus.OK);
    return responseEntity;
  }

  private ResponseEntity<CustomResponseDTO> newResponseEntityByCustomResponseDTO(Long id, String message, HttpStatus httpStatus) {
    CustomResponseDTO customResponseDTO = new CustomResponseDTO(id, message);
    return new ResponseEntity<>(customResponseDTO, httpStatus);
  }

  @ExceptionHandler(BusinessHttpStatusRuntimeException.class)
  public ResponseEntity<String> handleBusinessHttpStatusRuntimeException(BusinessHttpStatusRuntimeException ex) {
    HttpStatus httpStatus = ex.getHttpStatus();
    String errmsg = ex.getErrorMessage();
    //logger.warn("httpStatus:" + httpStatus.value() + "; " + errmsg);
    return new ResponseEntity<>(errmsg, httpStatus);
  }

  @ExceptionHandler(Throwable.class)
  public ResponseEntity<String> handleAllException(Throwable ext) {
    String error = Util.getErrorMessageByCauses(ext);
    return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
  }
}


8. Hibernate JPA DataSource/TransactionManager Configuration

JPAConfig.java class is for database hibernate JPA configuration:
package com.selimsql.springboot.config;

import java.util.Properties;

import javax.naming.NamingException;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.selimsql.springboot.util.Util;

@Configuration
@EnableTransactionManagement
@PropertySource(value = { "classpath:application.properties" })
public class JPAConfig {

  private Environment environment;

  public JPAConfig() {
    final String msg = "construct " + this.getClass().getSimpleName();
    System.out.println(msg);
  }

  @Autowired
  private void setEnvironment(Environment environment) {
    this.environment = environment;
  }

  @Bean
  public DataSource dataSource() {
    System.out.println("Bean: new DataSource");

    org.apache.tomcat.jdbc.pool.PoolProperties poolProperties = new org.apache.tomcat.jdbc.pool.PoolProperties();

    final String jdbcPREFIX = "jdbc";

    String driverClassName = environment.getProperty(jdbcPREFIX + ".driverClassName");
    poolProperties.setDriverClassName(driverClassName);

    String driverUrl = environment.getProperty(jdbcPREFIX + ".url");

    poolProperties.setUrl(driverUrl);
    System.out.println("->driverClassName:" + driverClassName);
    System.out.println("->driverUrl:" + driverUrl);

    String username = environment.getProperty(jdbcPREFIX + ".username");
    poolProperties.setUsername(username);

    String pass = environment.getProperty(jdbcPREFIX + ".password");
    poolProperties.setPassword(pass);

    final int initialSizeDEFAULT = 1;
    int initialSize = Util.getInt(environment.getProperty("jdbc.pool.initialSize"), initialSizeDEFAULT);
    if (initialSize > 0 && initialSize < 100) {
      poolProperties.setInitialSize(initialSize);
      poolProperties.setMinIdle(initialSize);

      System.out.println("->initialSize:" + initialSize);
    }

    final int maxActiveDEFAULT = 5;
    int maxActive = Util.getInt(environment.getProperty("jdbc.pool.maxActive"), maxActiveDEFAULT);
    if (maxActive >= initialSize && maxActive < 200) {
      poolProperties.setMaxIdle(maxActive); //Default: 100
      poolProperties.setMaxActive(maxActive); //Default: 100

      System.out.println("->maxActive:" + maxActive);
    }

    org.apache.tomcat.jdbc.pool.DataSource dsPooled = new org.apache.tomcat.jdbc.pool.DataSource(poolProperties);

    return dsPooled;
  }//data_Source

  @Bean
  public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
    System.out.println(">>>Bean:entityManagerFactory!!!");

    LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
    //factoryBean.setDataSource(dataSource());
    factoryBean.setDataSource(dataSource());

    factoryBean.setPackagesToScan(new String[] { "com.selimsql.springboot.model" });
    factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
    factoryBean.setJpaProperties(jpaProperties());
    return factoryBean;
  }

  @Bean
  public JpaVendorAdapter jpaVendorAdapter() {
    System.out.println(">>>Bean:jpaVendorAdapter!!!");

    HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();

    boolean showSql = true;
    adapter.setShowSql(showSql);

    adapter.setGenerateDdl(false);
    return adapter;
  }

  private Properties jpaProperties() {
    Properties properties = new Properties();
    String KEYS[] = new String[]
        { "hibernate.dialect",
        "hibernate.hbm2ddl.auto",
        "hibernate.ddl-auto",
        "hibernate.default_schema",
        "hibernate.id.new_generator_mappings",
        "hibernate.show_sql",
        "hibernate.format_sql",
        "hibernate.use_sql_comments"
        };

    int countKEY = KEYS.length;
    for (int i = 0; i < countKEY; i++) {
      String key = KEYS[i];
      String value = environment.getProperty(key);
      if (value != null)
        properties.put(key, value);
    }

    System.out.println("jpaProperties:" + properties);

    return properties;
  }

  @Bean
  public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
    System.out.println("Bean: new JpaTransactionManager:PlatformTransactionManager");

    PlatformTransactionManager platformTransactionManager = new JpaTransactionManager(entityManagerFactory);
    return platformTransactionManager;
  }
}


9. Spring Boot Application Starter

a) Application can be run as follow:
Right click on "AppStarter.java" file on project explorer. Choose "Run as", "Java Application" to start app.
package com.selimsql.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(scanBasePackages={"com.selimsql.springboot"})
public class AppStarter {

   private static final Class<?> APP_CLASS = AppStarter.class;
   private static final String   APP_NAME  = APP_CLASS.getSimpleName();

   public static void main(String[] args) {
     final String msg = "-------------------------\n" + APP_NAME + ".main";
     System.out.println(msg);

     SpringApplication springApp = new SpringApplication(APP_CLASS);
     springApp.run(args);

     System.out.println("SpringApplication.run:" + APP_NAME + " STARTED.");
  }
}

b) Test User REST Api Service:
Request-URL:
 http://localhost:8080/api/user/userList

Response as JSON format:
 [ {"id":1,"code":"admin","name":"Admin","surname":"Admin","password":"1111","phone":null,"status":1} ]




10. Main Page as Single Page Application

Application main page is index.html located in the src/main/resources/static folder.
This page works as single page application(SPA) on front end.
<!DOCTYPE html>
<html lang="en">

<head>
  <title>SpBoot REST App.</title>

  <meta charset="Utf-8">
  <meta http-equiv="Cache-Control" content="no-cache"/>
  <meta http-equiv="Cache-Control" content="must-revalidate"/>
  <meta http-equiv="Pragma"        content="no-cache"/>
  <meta http-equiv="Expires"       content="-1"/>

  <meta http-equiv='Content-Type'  content='text/html; charset=UTF-8'/>

  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <link rel="shortcut icon" type="image/x-icon" href="images/favicon.png">
  <link rel="stylesheet" href="css/myStyle.css">

  <script src="js/Common.js"></script>
  <script src="js/Home.js"></script>

  <script src="js/UserList.js"></script>
  <script src="js/UserAdd.js"></script>
  <script src="js/UserEdit.js"></script>
</head>

<body onload="fixContentDiv()">
  <div class="containerHeader">
    <h3>SpringBoot Rest Application</h3>
  </div>

  <ul class="topMenu">
    <li>
      <button class="buttonTop" onclick="renderHomePage()">Home</button>
    </li>
    <li>
      <button class="buttonTop" onclick="renderUserList(contentDiv)">User List</button>
    </li>
    <li class="right">
      <button class="buttonTop" onclick="alert('SpringBoot Rest Application, Version:1.0')">About</button>
    </li>
  </ul>

  <div id="content" class="containerBody">
    Wellcome
  </div>

</body>

</html>


Test it by entering this URL: http://localhost:8080/index.html





11. Add User

UserAdd.js and Common.js are JavaScript pages called from main page by clicking Add User button:
These pages are located in the src/main/resources/static/js folder.
  Common.js

  var contentDiv = null; //Global param.
  var requestUrlPrefix = null; //Global param.
  
  function fixContentDiv() {
      contentDiv = document.getElementById("content");
  }
  
  function fixRequestUrlPrefix() {
      if (requestUrlPrefix!=null)
          return requestUrlPrefix;
  
      const locProtocol = window.location.protocol; //"file:", "http:"
      if (locProtocol.startsWith("file"))
          requestUrlPrefix = "http://localhost:8080/";
      else
          requestUrlPrefix = "";
  
      return requestUrlPrefix;
  }
  
  function updateContent(execFunc) {
      contentDiv.innerHTML = execFunc();
  }
  
  function updateContentAfterExecFunc(execFunc) {
      execFunc(contentDiv);
  }
  UserAdd.js

  function renderUserAdd(contentDiv) {
    if (typeof contentDiv=="undefined") {
      alert("contentDiv is undefined!");
      return;
    }

    //-----------------
    //const user = {}; //Empty row!

    var text = [];
    text.push('<h1>User Add</h1>');

    text.push('<br/>');
    text.push('<form>');
    text.push('<table>');

    text.push('<tr>');
    text.push('<th align="right">Id (*)</th>');
    text.push('<td>[Automatic]<td/>');
    text.push('</tr>');

    text.push('<tr>');
    text.push('<th align="right">Code (*)</th>');
    text.push('<td><input type="text" name="code" value="" required/><td/>');
    text.push('</tr>');

    text.push('<tr>');
    text.push('<th align="right">Name (*)</th>');
    text.push('<td><input type="text" name="name" value="" required/><td/>');
    text.push('</tr>');

    text.push('<tr>');
    text.push('<th align="right">Surname (*)</th>');
    text.push('<td><input type="text" name="surname" value="" required/><td/>');
    text.push('</tr>');

    text.push('<tr>');
    text.push('<th align="right">Password (*)</th>');
    text.push('<td><input type="password" name="password" value="" required/><td/>');
    text.push('</tr>');

    text.push('<tr>');
    text.push('<th align="right">Phone</th>');
    text.push('<td><input type="text" name="phone" value=""/><td/>');
    text.push('</tr>');

    text.push('<tr>');
    text.push('<th align="right">Status (*)</th>');

    text.push('<td>');

    text.push('<select name="status">');
    text.push(' <option value="1">Active</option>');
    text.push(' <option value="0">Passive</option>');
    text.push('</select>');

    text.push('</td>');
    text.push('</tr>');

    text.push('</table>');

    text.push('<br/><input type="button" onclick="saveUserAdd(contentDiv, this.form)" value="Save"/>');
    text.push(' ');
    text.push('<button type="button" onclick="renderUserList(contentDiv)">Cancel</button>');
    text.push('</form>');

    contentDiv.innerHTML = text.join("");
}


function saveUserAdd(contentDiv, frm) {
  const frmElements = frm.elements;
  const elementCount = frmElements.length;

  const user = {}; //Empty row!

  for(let i = 0; i < elementCount; i++) {
    let inObj = frmElements[i];
    let inObjName = inObj.name;
    if (inObjName==null || inObjName=="")
      continue;

    let isValid = inObj.checkValidity();

    if (isValid == false) {
        alert(inObjName + ", error:" + inObj.validationMessage);
        return;
    }

    user[inObjName] = inObj.value;
  }//for

  //---------------------
  const xmlhttp = new XMLHttpRequest();

  xmlhttp.onreadystatechange = function() {
    if (this.readyState == XMLHttpRequest.DONE) {
      if (this.status == 201) {
        alert("Addition is successful");
        renderUserList(contentDiv);
      }
      else {
        alert("Add Result Error:" + this.responseText);
      }
    }
  };


  //---------------------
  const requestUrl = fixRequestUrlPrefix() + "api/user/add";

  xmlhttp.open("POST", requestUrl, true);

  //Note: HTTP headers can be used to describe the content being sent or requested within an HTTP request.
  xmlhttp.setRequestHeader("Content-Type", "application/json");
  xmlhttp.setRequestHeader("Accept", "application/json");

  const userAddStr = JSON.stringify(user);
  xmlhttp.send(userAddStr);
}//save_UserAdd

Test App Page:






12. Update/Delete/View User

UserEdit.js is a JavaScript page called from main page by clicking one of Update/Delete/View buttons:
This page is located in the src/main/resources/static/js folder.
  var userLast = null; //Global param:user
  var operation = null; //Global param: "Update", "Delete", "View"

  function renderUserUpdate(contentDiv, userId) {
    operation = "Update";
    return renderUserEdit(contentDiv, userId);
  }

  function renderUserDelete(contentDiv, userId) {
    operation = "Delete";
    return renderUserEdit(contentDiv, userId);
  }

  function renderUserView(contentDiv, userId) {
    operation = "View";
    return renderUserEdit(contentDiv, userId);
  }

  function renderUserEdit(contentDiv, userId) {
      if (typeof contentDiv=="undefined") {
        alert("contentDiv is undefined!");
        return;
      }

      if (typeof userId=="undefined") {
        alert("userId is undefined!");
        return;
      }

      //-----------------
      const xmlhttp = new XMLHttpRequest();
      xmlhttp.onreadystatechange = function() {
        if (this.readyState == XMLHttpRequest.DONE) {
          var respHtml;
          if (this.status == 200) {
            userLast = JSON.parse(this.responseText);
            respHtml = prepareUserEdit(userLast);
          }
          else {
            //const error = JSON.parse(this.responseText);
            const error = this.responseText;
            respHtml = "Status:" + this.status + "<br>Error:" + error;
          }

          contentDiv.innerHTML = respHtml;
        }//if XMLHttpRequest.DONE
      };

      //------------------------------
      const requestUrl = fixRequestUrlPrefix() + "api/user/userRow/" + userId;

      xmlhttp.open("GET", requestUrl, true);

      //Note: HTTP headers can be used to describe the content being sent or requested within an HTTP request.
      //xmlhttp.setRequestHeader("Content-Type", "application/json");
      xmlhttp.setRequestHeader("Accept", "application/json");

      xmlhttp.send();
  }//render_UserEdit


  function prepareUserEdit(user) {
    var text = [];
    text.push('<h1>User ' + operation + '</h1>');
    text.push('<br/>');
    text.push('<form>');
    text.push('<table>');

    text.push('<tr>');
    text.push('<th align="right">Id (*)</th>');
    text.push('<td>' + user.id + '<td/>');
    text.push('</tr>');

    text.push('<tr>');
    text.push('<th align="right">Code (*)</th>');
    text.push('<td><input type="text" name="code" value="' + user.code + '" required/><td/>');
    text.push('</tr>');

    text.push('<tr>');
    text.push('<th align="right">Name (*)</th>');
    text.push('<td><input type="text" name="name" value="' + user.name + '" required/><td/>');
    text.push('</tr>');

    text.push('<tr>');
    text.push('<th align="right">Surname (*)</th>');
    text.push('<td><input type="text" name="surname" value="' + user.surname + '" required/><td/>');
    text.push('</tr>');

    text.push('<tr>');
    text.push('<th align="right">Password (*)</th>');
    text.push('<td><input type="password" name="password" value="' + user.password + '" required/><td/>');
    text.push('</tr>');

    let userPhone = user.phone;
    if (userPhone==null)
      userPhone = "";

    text.push('<tr>');
    text.push('<th align="right">Phone</th>');
    text.push('<td><input type="text" name="phone" value="' + userPhone + '"/><td/>');
    text.push('</tr>');

    text.push('<tr>');
    text.push('<th align="right">Status (*)</th>');

    text.push('<td>');

    text.push('<select name="status">');

    const statusArray = [{key:1, value:"ACTIVE"}, {key:0, value:"PASSIVE"}];

    let userStatus = user.status;
    for(let i = 0; i < statusArray.length; i++) {
      let statusObj = statusArray[i];
      let statusKey = statusObj.key;
      let strOption = "<option value='" + statusKey + "'";

      if (statusKey == userStatus)
        strOption += " selected";

      strOption += ">" + statusObj.value + "</option>";

      text.push(strOption);
    }//for-i

    text.push('</select>');

    text.push('</td>');
    text.push('</tr>');

    text.push('</table>');

    text.push('<br/>');
    let backTitle = "Cancel";
    if (operation=="Update" || operation=="Delete") {
      text.push('<input type="button" onclick="saveUser' + operation + '(contentDiv, this.form, userLast)" value="' + operation + '"/>');
      text.push(' ');
    }
    else {
      backTitle = "OK";
    }

    text.push('<button type="button" onclick="renderUserList(contentDiv)">' + backTitle + '</button>');
    text.push('</form>');

    return text.join("");
  }//prepare_UserEdit


  function saveUserUpdate(contentDiv, frm, user) {
    const frmElements = frm.elements;
    const elementCount = frmElements.length;

    for(let i = 0; i < elementCount; i++) {
      var inObj = frmElements[i];
      var inObjName = inObj.name;
      if (inObjName==null)
        continue;

      if (inObj.checkValidity() == false) {
          alert(inObjName + ", error:" + inObj.validationMessage);
          return;
      }

      user[inObjName] = inObj.value;
    }//for

    const userNewStr = JSON.stringify(user);

    //---------------------
    const xmlhttp = new XMLHttpRequest();

    xmlhttp.onreadystatechange = function() {
      if (this.readyState == XMLHttpRequest.DONE) {
        if (this.status == 200) {
          alert("Update is successful");
          renderUserList(contentDiv);
        }
        else {
          alert("Update Result Error:" + this.responseText);
        }
      }
    };


    //---------------------
    const requestUrl = fixRequestUrlPrefix() + "api/user/update/" + user.id;

    xmlhttp.open("PUT", requestUrl, true);

    //Note: HTTP headers can be used to describe the content being sent or requested within an HTTP request.
    xmlhttp.setRequestHeader("Content-Type", "application/json");
    xmlhttp.setRequestHeader("Accept", "application/json");

    xmlhttp.send(userNewStr);
  }//save_UserUpdate


  function saveUserDelete(contentDiv, frm, user) {
    const userId = user.id;

    const xmlhttp = new XMLHttpRequest();

    xmlhttp.onreadystatechange = function() {
      if (this.readyState == XMLHttpRequest.DONE) {
        if (this.status == 200) {
          alert("Delete is successful");
          renderUserList(contentDiv);
        }
        else {
          alert("Delete Result Error:" + this.responseText);
        }
      }
    };


    //---------------------
    const requestUrl = fixRequestUrlPrefix() + "api/user/delete/" + userId;

    xmlhttp.open("DELETE", requestUrl, true);

    xmlhttp.setRequestHeader("Content-Type", "application/json");
    xmlhttp.setRequestHeader("Accept", "application/json");

    xmlhttp.send("userId=" + userId);
  }//save_UserDelete

Test Edit Page:





Download Project Example

Download source: SpringBootRESTHibernateJPAJavaScriptSSqlCRUDExample.zip
File size: 32KB


References



Related Posts

Related Posts


Android Mobile Apps

Copyright © 2009 - 2019 SelimSql