Wrap-up Lab for Java/Spring QuickStart ------------------------------------- The goal is to do a Spring Boot JSON endpoint and a React.js front end. Part 1: Generating a Spring Boot Starter Project ------------------------------------------------- One problem is that the Protech lab machines have Java 11, but Spring Boot v3 requires Java 17 and the Initializr at start.spring.io only supports Boot v3. Here's how to generate and and configure a Spring Boot project that uses Spring Boot version 2 that will work on Java 11. 1. Go to https://start.spring.io/ and choose: - Project: Maven - Language: Java - Java: 17 Click the "Add Dependencies" button on the right side of the page and add dependencies for h2, jdbc, web. Generate the zip, download it and unzip it into the StudentWork\Spring directory -- this should create a subdirectory named "demo". 2. In Eclipse, import Existing Maven Project from the demo directory. There will be errors! 3. Right-click on project in Project Explorer and choose Properties, then: - Java Build Path: - Click Libraries tab - Highlight Java 17 and click Edit - Change to Java 11 JDK - Java Compiler, change all 17 to 11 Apply and close. Errors should go away 4. Open pom.xml and: - Change spring-boot-starter-parent to 2.7.18 - Change to 11 5. Still might have an error in Markers tab. Right-click on error message and choose Quick Fix. 6. Open a cmd prompt and change to the demo directory. Then build and run using: mvnw spring-boot:run Maven should download the updated dependencies and run the project without error. Press Ctrl+C on the window to terminate the application. Part 2: Writing the Spring Boot JSON Resource ---------------------------------------------- In this part, you will write a Spring Boot server-side application that retrieves Customer data from a database and exposes it as a JSON RESTful resource so a JavaScript application can consume it. 1. Now let's build the Java domain class for the application, which will display a list of customers: In a new com.example.demo.domain package, create a class named Customer with the following fields: private int customerID; private String name; private String email; private String phone; Generate get/set methods and a toString() method. 2. Next, create the data layer for the application: a. In a new com.example.demo.dao package, create a new interface named CustomerDao. Add the following methods: List allCustomers(); b. In the same package, create a class named CustomerDaoImpl that implements the interface. c. Annotate the class with @Repository. d. Within the class, outside of the methods, write an inner class that maps JDBC rows to Customer objects: private class CustomerRowMapper implements RowMapper { public Customer mapRow(ResultSet rs, int rowNum) throws SQLException { Customer cust = new Customer(); cust.setCustomerID(rs.getInt("CustomerID")); cust.setEmail(rs.getString("Email")); cust.setName(rs.getString("Name")); cust.setPhone(rs.getString("Phone")); return cust; } } e. After the inner class, define fields in the DAO class: private JdbcTemplate template; private CustomerRowMapper mapper; private DataSource customerDataSource; f. After the fields, inject the data source: @Autowired public void setDataSource(DataSource ds) { customerDataSource = ds; template = new JdbcTemplate(ds); mapper = new CustomerRowMapper(); } g. Complete the allCustomers() method: @Override public List allCustomers() { List customers = template .query("SELECT * FROM Customer", mapper); return customers; } h. Use Source -> Organize imports menu so Eclipse writes appropriate import statements. You should have no errors at this point. 3. Next, create a REST resource that provides the customer list as JSON: a. In a new com.example.demo.controllers package, create a class named CustomerController. b. Annotate the class with @Controller. c. Autowire a reference to the DAO: private CustomerDao dao; @Autowired public CustomerController(CustomerDao dao) { this.dao = dao; } d. Write a method that returns all customers as a RESTful resource with a URL that ends in "/customer": @RequestMapping(value = "/customer", method = RequestMethod.GET) public @ResponseBody List getCustomers() { return dao.allCustomers(); } e. Still in the CustomerController class, but outside any method, write a nested class that serves up 404 NOT FOUND errors: @ResponseStatus(value = HttpStatus.NOT_FOUND) private class ResourceNotFoundException extends RuntimeException { } h. Use Source -> Organize imports menu so Eclipse writes appropriate import statements. You should have no errors at this point. 4. Next, create an SQL script that will create the Customer table and populate it with a couple of rows. In the src/ main/resources folder, create a file named schema.sql with the following contents: CREATE TABLE customer(customerID INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name VARCHAR(100), phone VARCHAR(100), email VARCHAR(100)); INSERT INTO customer (name, phone, email) VALUES ('Sam Smith', '555-1212', 'sam@sam.com'); INSERT INTO customer (name, phone, email) VALUES ('Sue Jones', '555-1313', 'sue@sue.com'); Spring Boot will run this script to populate an in-memory H2 database every time the application runs. 5. As a quick test, go back to the command prompt and run mvnw spring-boot:run again. Then open a browser and navigate to: http://localhost:8080/customer You should see the customer list in JSON format. You can close the browser, but leave the Spring boot application running in the command prompt. Part 3: Writing a React.JS Front End ------------------------------------- 1. Open a new command-prompt window and enter: node -v If you don't see a Node version number, go to https://nodejs.org and download and install Node using all of the default options. 2. In the command prompt, see if VSCode is installed: code . If that doesn't work, go to https://code.visualstudio.com and download and install VS Code using all of the default options. 3. In the command-prompt, change to the StudentWork\Spring folder and then use node and vite to create a React project: npm create vite@latest react-customers -- --template react Then change to the react-customers directory and install and do a test: npm install npm run dev 4. Edit App.jsx and delete all the imports except for useState and delete all of the text within the <> . Update the React import so it also brings in useEffect. Change the useState() call so it defines an array of customers as the component's state, initializing to an empty array: const [customers, setCustomers] = useState([]); 5. Add a useEffect() function in which you request the Customers JSON from the Spring Boot service: useEffect(() => { async function fetchData() { try { const response = await fetch( 'http://localhost:8080/customer' ); const json = await response.json(); setCustomers(json); } catch (e) { console.error(e); } } fetchData(); }, []); 6. Execute "npm run dev" in the command prompt and open a web browser at http://localhost:5173/. Open the browser's developer tools and look in the console. Did you get a CORS error? The problem is that the React web app and the Spring Boot REST server use different HTTP ports (8080 and 5173) so by default, the browser blocks the AJAX fetch. 7. To fix the CORS error, in Eclipse, add this annotation to the getCustomers() method in CustomerController: @CrossOrigin This causes Spring Web to return lenient CORS policy HTTP response headers for this endpoint. Save to compile and then kill the Spring application in its command prompt and restart it. Refresh the browser running the Vite app and look in the console - the CORS errors should be gone (the web page doesn't display anything yet). 8. Create a Customer component as a functional component that accepts "props" as an argument (it will be a customer object). The function should return JSX that: - Displays the customer's properties in a string inside of
  • and
  • tags 9. Create a CustomerList component as a functional component that accepts "props" as an argument (it will be the customer array). The function should return JSX that: - Displays a starting
      tag - Iterates the customer array using Array.map() For each array element, render a child component, passing a customer object as "props" - Displays an ending
    tag 10. Update the App component to render the component, passing the customer list state as "props". You will need to import the component. 11. Run and test in the browser. Use the browser console in the Developer Tools to help diagnose problems.