ECE366 - Lesson 6
Intro to Javascript and React
Instructor: Professor Hong
## Java Persistence API (JPA)
- Standard Java EE (Jakarta EE) specification for ORM (Object–Relational Mapping)
- Allows you to map between objects and database tables
- Streamlines persistence to standard format
- Reduces JDBC code
- Focus on OOP
## application.properties
```
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:postgresql://${POSTGRES_HOST}:5432/${POSTGRES_DB}
spring.datasource.username=postgres
spring.datasource.password=${POSTGRES_PASSWORD}
```
## Hard coded
```
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:postgresql://localhost:5432/rps
spring.datasource.username=postgres
spring.datasource.password=password
```
## pom.xml
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
```
## Important Annotations
- ```@RestController``` - enables API endpoints
- ```@Entity``` - a row from the database
- ```@Table``` - the table name
- ```@Id``` - the ID of a table row
- ```@Column``` - a column of the table
- ```@GeneratedValue``` - strategies for primary key
## Code Organization for JPA
## User Class
data/User.java
```
package com.chrishong.rps.data;
import jakarta.persistence.*;
@Entity
@Table(name="USERS")
public class User {
@Id
@Column(name="USER_ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long userId;
@Column(name="USER_NAME")
private String userName;
@Column(name="PASSWORD")
private String password;
@Column(name="TOTAL_GAMES")
private int totalGames;
@Column(name="TOTAL_WIN")
private int totalWin;
@Column(name="TOTAL_LOSS")
private int totalLoss;
@Column(name="TOTAL_TIE")
private int totalTie;
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getTotalGames() {
return totalGames;
}
public void setTotalGames(int totalGames) {
this.totalGames = totalGames;
}
public int getTotalWin() {
return totalWin;
}
public void setTotalWin(int totalWin) {
this.totalWin = totalWin;
}
public int getTotalLoss() {
return totalLoss;
}
public void setTotalLoss(int totalLoss) {
this.totalLoss = totalLoss;
}
public int getTotalTie() {
return totalTie;
}
public void setTotalTie(int totalTie) {
this.totalTie = totalTie;
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", userName='" + userName + '\'' +
", password='" + password + '\'' +
", totalGames=" + totalGames +
", totalWin=" + totalWin +
", totalLoss=" + totalLoss +
", totalTie=" + totalTie +
'}';
}
}
```
## UserRepository Interface
data/UserRepository.java
```
package com.chrishong.rps.data;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
```
## UserService
business/UserService
```
package com.chrishong.rps.business;
import com.chrishong.rps.data.User;
import com.chrishong.rps.data.UserRepository;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public List getUsers(){
Iterable users = this.userRepository.findAll();
List userList = new ArrayList<>();
users.forEach(user->{userList.add(user);});
return userList;
}
}
```
## WebserviceController
webservice/WebserviceController.java
```
package com.chrishong.rps.webservice;
import com.chrishong.rps.business.UserService;
import com.chrishong.rps.data.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class WebserviceController {
private final UserService userService;
public WebserviceController(UserService userService) {
this.userService = userService;
}
@GetMapping("/users")
public List getUsers(){
System.out.println("getUsers");
return this.userService.getUsers();
}
@GetMapping("/testuser")
public String getTestUser() {
return "TEST USERS";
}
}
```
## Dockerfile
```
FROM maven:3.9.6-eclipse-temurin-21 AS build
ADD . /project
WORKDIR /project
RUN mvn -e -Dmaven.test.skip package
FROM eclipse-temurin:latest
COPY --from=build /project/target/rpsjpa-0.0.1-SNAPSHOT.jar /app/rps.jar
ENTRYPOINT java -jar /app/rps.jar
```
- Note, the ```-Dmaven.test.skip``` skips tests during the build
## Docker Compose
```
services:
db:
image: postgres
volumes:
- $HOME/srv/postgres:/var/lib/postgresql/data
environment:
- POSTGRES_DB=postgres
- POSTGRES_PASSWORD=password
expose:
- 5432:5432
ports:
- 5432:5432
restart: always
app:
build: .
environment:
- POSTGRES_HOST=db
- POSTGRES_DB=rps2
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
expose:
- 8080:8080
ports:
- 8080:8080
depends_on:
- db
```
- Don't forget to update the applications.properties file!
## What is HTML?
- HyperText Markup Language (not a programming language)
- Standard markup language for website creation
- Maintained World Wide Web Consortium (W3C)
- Parts - opening tag, content, closing tag
## New pom.xml Dependency
- Building upon last week's JPA example
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
```
- Thymeleaf templates allow you to create basic web applications
## Web Controller
web/UserController
```
package com.chrishong.rps.web;
import com.chrishong.rps.business.UserService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@RequestMapping(method = RequestMethod.GET)
public String getUsers(Model model){
model.addAttribute("users", this.userService.getUsers());
return "rps-users";
}
}
```
## Webservice API
webservice/WebserviceController
```
package com.chrishong.rps.webservice;
import com.chrishong.rps.business.UserService;
import com.chrishong.rps.data.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api")
public class WebserviceController {
private final UserService userService;
public WebserviceController(UserService userService) {
this.userService = userService;
}
@GetMapping("/users")
public List getUsers(){
System.out.println("getUsers");
return this.userService.getUsers();
}
}
```
## Index.html
resources/static/index.html
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>RPS</title>
</head>
<body>
<h1>Welcome to RPS</h1>
<p><a href="/users">Users</a></p>
</body>
</html>
```
## rps-users.html
resources/templates/rps-users.html
```
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>RPS: Users</title>
</head>
<body>
<h1>RPS Users</h1>
<table>
<tr>
<td>User ID</td>
<td>User Name</td>
<td>Password</td>
<td>Total Wins</td>
</tr>
<tr th:each="user: ${users}">
<td th:text="${user.userId}">UserId</td>
<td th:text="${user.userName}">UserName</td>
<td th:text="${user.password}">Password</td>
<td th:text="${user.totalGames}">TotalGames</td>
</tr>
</table>
</body>
</html>
```
## Running the Site
```
localhost:8080
localhost:8080/users
localhost:8080/api/users
```
You can read more here: [https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html](https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html)
## What is React?
- Javascript library
- Created at Facebook
- Open-sourced in 2013
- Docs: [https://react.dev/reference/react](https://react.dev/reference/react)
## What is Javascript?
- High level, interpreted language
- Allows you to implement complex features on web pages
- Used with HTML and CSS (for styling)
- Logging: ```console.log();```
## Setup/Installation
- Add React Developer Tools from Chrome Extensions and add to chrome
- Inspect Element -> Components
- Can also install on Firefox
## React in Index.html
```
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script
src="https://unpkg.com/react@17/umd/react.development.js"
crossorigin
></script>
<script
src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"
crossorigin
></script>
<title>React ⚛️</title>
</head>
<body>
<h1>Getting Started with React</h1>
</body>
</html>
```
## React in Index.html
```
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
<title>React ⚛️</title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript">
ReactDOM.render(
React.createElement("h1", null, "Getting Started!"),
document.getElementById("root")
);
</script>
</body>
</html>
```
## ReactDOM.render
ReactDOM.render() - 2 arguments
- what to create: React.createElement("h1", null, "TEXT")
- where to put it: document.getElementById("root")
## Variable Names
```
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script
src="https://unpkg.com/react@17/umd/react.development.js"
crossorigin
></script>
<script
src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"
crossorigin
></script>
<title>React ⚛️</title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript">
let heading = React.createElement(
"h1",
{ style: { color: "blue" } },
"Heyyyy Everyone!"
);
ReactDOM.render(
heading,
document.getElementById("root")
);
</script>
</body>
</html>
```
## JSX, Babel
```
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<title>React ⚛️</title>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
ReactDOM.render(
<ul>
<li>🤖</li>
<li>🤠</li>
<li>🌝</li>
</ul>,
document.getElementById("root")
);
</script>
</body>
</html>
```
- Javascript XML
- Can access variables with ```{``` variable ```}```
## More JSX syntax
```
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script
src="https://unpkg.com/react@17/umd/react.development.js"
crossorigin
></script>
<script
src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"
crossorigin
></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<title>React ⚛️</title>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
let robot = "🤖";
let cowboy = "🤠";
let moon = "🌝";
let name = "React";
ReactDOM.render(
<ul>
<li>{robot}</li>
<li>{cowboy}</li>
<li>{moon}</li>
<li>{name.toUpperCase()}</li>
<li>{name.length}</li>
</ul>,
document.getElementById("root")
);
</script>
</body>
</html>
```
## React Components
- A function that returns JSX and can be rendered in DOM
- Should use capital letter for component/function name
- Multiple React components should be wrapped into 1 component
- Can be used to display dynamic data
## Component Properties
- Add props argument to component functions
- A container where you can put any properties in the component
## Example with React Components and Image
```
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script
src="https://unpkg.com/react@17/umd/react.development.js"
crossorigin
></script>
<script
src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"
crossorigin
></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<title>RPS</title>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function Header(props) {
return (
<header>
<h1>{props.name}'s RPS</h1>
</header>
);
}
function Main(props) {
return (
<section>
<img
height={200}
src="./rps.png"
alt="Rock Paper Scissors"
/>
<ul>
{props.users.map((user) => (
<li key={user.id}>
{user.title}
</li>
))}
</ul>
</section>
);
}
function Footer(props) {
return (
<footer>
<p>Copyright {props.year}</p>
</footer>
);
}
const users = [
"Alpha",
"Beta",
"Gamma",
];
const userObjects = users.map(
(user, i) => ({
id: i,
title: user
})
);
function App() {
return (
<React.Fragment>
<Header name="Cooperps" />
<Main
adjective="amazing"
users={userObjects}
/>
<Footer
year={new Date().getFullYear()}
/>
</React.Fragment>
);
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
</script>
</body>
</html>
```
## Create React App
- Install nodejs - [https://nodejs.org/](https://nodejs.org/)
- nodejs on WSL - [https://learn.microsoft.com/en-us/windows/dev-environment/javascript/nodejs-on-wsl](https://learn.microsoft.com/en-us/windows/dev-environment/javascript/nodejs-on-wsl)
- Remove old version of node:
```
sudo apt-get purge --auto-remove nodejs
```
- ```node -v``` - to check node version
- ```npm -v``` - check npm version
- ```npx create-react-app rps```
- ```npm start```
## Important Parts
- package.json - dependencies - react, react-dom, react-scripts
- src/index.js - entry point of the application, main JS file
- renders App
- React.StrictMode - additional checks
- public/index.html - similar to index.html w/ all your JSX injected
```
npm install
npm start
```
## Javascript Destructuring
Allows you to get specific elements from an argument/props
```
function App({ myVariable }) {
return (
{myVariable
);
}
```
- Also Array destructuring allows you to get certain elements from an array
## Changing States with useState
App.js
```
import "./App.css";
import { useState } from "react";
function App() {
const [emotion, setEmotion] = useState("happy");
return (
<div className="App">
<h1>Current emotion is {emotion}</h1>
<button onClick={() => setEmotion("sad")}>
Sad
</button>
<button
onClick={() => setEmotion("excited")}
>
Excited
</button>
</div>
);
}
export default App;
```
## Side Effects with useEffect
```
import "./App.css";
import { useState, useEffect } from "react";
function App() {
const [emotion, setEmotion] = useState("happy");
const [secondary, setSecondary] =
useState("tired");
useEffect(() => {
console.log(`It's ${emotion} around here!`);
}, [emotion]);
useEffect(() => {
console.log(`It's ${secondary} around here!`);
}, [secondary]);
return (
<div className="App">
<h1>Current emotion is {emotion}</h1>
<button onClick={() => setEmotion("sad")}>
Sad
</button>
<button
onClick={() => setEmotion("excited")}
>
Excited
</button>
<h2>
Current secondary emotion is {secondary}.
</h2>
<button
onClick={() => setSecondary("grateful")}
>
Grateful
</button>
</div>
);
}
export default App;
```
- dependencyArray is the 2nd argument for useEffect
- No argument makes it called everytime
- Empty array makes it called once
## Fetching Data from Service
fetch is built into the browser to make an HTTP request to get data
```
import "./App.css";
import { useState, useEffect } from "react";
function App() {
const [data, setData] = useState(null);
useEffect(() => {
fetch(
`http://localhost:8080/api/users`
)
.then((response) => response.json())
.then(setData);
}, []);
if (data)
return (
<pre>{JSON.stringify(data, null, 2)}</pre>
);
return <h1>Data</h1>;
}
export default App;
```
## Update CORS in Spring Boot server
WebserviceController.java
```
@CrossOrigin
@GetMapping("/users")
```