Spring Example with VS Code

From bibbleWiki
Jump to navigation Jump to search

Introduction

To make sure my Java is keeping up with my Typescript I revisited Spring to see what it would take to build a REST API using the Spring Web framework. The goal was

  • Create a REST API
  • Create GET endpoints
  • Use a database in Spring
  • Add Filtering to endpoint
  • Add OAuth to endpoint

Installation

This was remarkably easy.

Install Java

sudo apt install default-jdk -y

Install Maven

We grab lastest and put it in /opt

  wget https://dlcdn.apache.org/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz
  sudo tar xf apache-maven-3.9.9-bin.tar.gz -C /opt

Make a profile in /etc/profile.d/maven.sh

export JAVA_HOME=/usr/lib/jvm/default-java 
export M3_HOME=/opt/apache-maven-3.9.9
export MAVEN_HOME=/opt/apache-maven-3.9.9
export PATH=${M3_HOME}/bin:${PATH}

Now we can have an environment with . /etc/profile.d/maven.sh

VS Code Setup

For me I installed

  • Java Extension Pack
  • Spring Boot Extension Pack
  • Spring Initializer

Create New Project

You can do this using the Spring Initializer extension. For this project we need

  • spring-boot-starter-web
  • spring-boot-starter-data-jpa

For me the main change was to rename application.properties to application.yml so I could configure the project better. This included fixing the port and and the request header size

spring:
  application:
    name: springbibble
server:
  port: 8082
  max-http-request-header-size: 10MB

Making an Endpoint

This again could not be easier. All I had to do was in the Java Project explorer, press plus against my application and it prompted me to add a class. Type in pingController and we were away. Just add annotations to controller and endpoint

@RestController
public class pingController {

    @GetMapping("/ping")
    public String ping() {
        return "pong";
    }
}

Adding Snowflake

My last place used snowflake and wasn't keen but this is odd enough to demonstrate how to do it for anything. In pom.xml, add the dependency.

		<dependency>
			<groupId>net.snowflake</groupId>
    		<artifactId>snowflake-jdbc</artifactId>
    		<version>3.13.34</version>
		</dependency>

Then we need to specify the credential in the application.yml

spring:
  application:
    name: abrat

  datasource:
    username: ${SNOWFLAKE_USERNAME}
    password: ${SNOWFLAKE_PASSWORD}
    driverClassName: net.snowflake.client.jdbc.SnowflakeDriver
    url: jdbc:snowflake://${SNOWFLAKE_ACCOUNT}.snowflakecomputing.com/?db=${SNOWFLAKE_DATABASE}&schema=${SNOWFLAKE_SCHEMA}&warehouse=${SNOWFLAKE_WAREHOUSE}&role=${SNOWFLAKE_ROLE}

Making a proper endpoint

To model my approach used in Typescript we will have

  • Controller
  • Service
  • Repository
  • Entity

None of these are difficult and with Spring a lot of the code comes for fred

Controller

package local.bibble.brat.controllers;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import local.bibble.brat.entities.Area;
import local.bibble.brat.services.AreaService;

@RestController
@RequestMapping("/api/areas")
public class areaController {

    @Autowired
    private AreaService areaService;

    @GetMapping
    public List<Area> getLocationId(@RequestParam("id") Long id) {
        return areaService.findByLocationId(id);
    }

}

Service

package local.bibble.brat.services;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import local.bibble.brat.entities.Area;
import local.bibble.brat.repositories.AreaRepository;

@Service
public class AreaService {

    @Autowired
    AreaRepository areaRepository;

    public List<Area> findByLocationId(Long id) {
        return areaRepository.getOneByLocationId(id);
    }
}

Repository

package local.bibble.brat.repositories;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

import local.bibble.brat.entities.Area;

public interface AreaRepository extends JpaRepository<Area, Long> {
    public List<Area> getOneByLocationId(Long id);
}

Entity

package local.bibble.brat.entities;

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

@Entity
@Table(name = "LOCATION")
public class Area {
    @Id
    @Column(name = "TLOCATION_ID")
    private Long locationId;

    @Column(name = "TLOCATION_NAME")
    private String locationName;

    public Long getLocationId() {
        return locationId;
    }

    public String getLocationName() {
        return locationName;
    }
}

Problem 1

So getting this to work should have been straight forward but getting back into Maven, Pom and java meant getting something up and running was 4 hours instead of one. The problem was how to pass options to Maven as the JDBC driver was broken.

_JAVA_OPTIONS="--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED" mvn spring-boot:run

Setting Java Options

You can set the arguments passed to the jvm in the pom.xml using the jvmArguments

...
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
    				<jvmArguments>--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED</jvmArguments>
  				</configuration>
			</plugin>
		</plugins>
	</build>

Running the example

We need to

  • initialize maven
  • set up environment variable
  • set _JAVA_OPTIONS
. /etc/profile.d/maven.sh
. .env.local
# If you haven't set this in the pom
# _JAVA_OPTIONS="--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED" mvn spring-boot:run