Spring Example with VS Code
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