Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Link performance test in table of contents. See [Documentation Table of Contents](https://github.com/awslabs/aws-advanced-jdbc-wrapper/blob/main/docs/Documentation.md).
- Cluster URLs are not internally pooled. See [Using Read Write Splitting Plugin Internal Connection Pooling](https://github.com/awslabs/aws-advanced-jdbc-wrapper/blob/main/docs/using-the-jdbc-driver/using-plugins/UsingTheReadWriteSplittingPlugin.md#internal-connection-pooling).
- The `leastConnections` strategy in the [Using Read Write Splitting Plugin Internal Connection Pooling](https://github.com/awslabs/aws-advanced-jdbc-wrapper/blob/main/docs/using-the-jdbc-driver/using-plugins/UsingTheReadWriteSplittingPlugin.md#internal-connection-pooling) at point #3.
- Sample code and tutorial for using the driver with:
- [Spring and Hibernate](./examples/SpringHibernateExample/README.md)
- [Spring and Wildfly](./examples/SpringWildflyExample/README.md)

### :bug: Fixed
- Pruned null connections in connection tracker plugins ([PR #461](https://github.com/awslabs/aws-advanced-jdbc-wrapper/pull/461))
Expand Down
2 changes: 1 addition & 1 deletion docs/using-the-jdbc-driver/FailoverConfigurationGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ A failover time profile refers to a specific combination of failover parameters
| `failoverClusterTopologyRefreshRateMs` | `2000` |

### Writer Cluster Endpoints After Failover
Connecting to a writer cluster endpoint after failover can result in a faulty connection because DNS causes a delay in changing the writer cluster. On the AWS DNS server, this change is updated usually between 15-20 seconds, but the other DNS servers sitting between the application and the AWS DNS server may not be updated in time. Using this stale DNS data will most likely cause problems for users, so it is important to keep this is mind.
Connecting to a writer cluster endpoint after failover can result in a faulty connection because DNS causes a delay in changing the writer cluster. On the AWS DNS server, this change is updated usually between 15-20 seconds, but the other DNS servers sitting between the application and the AWS DNS server may not be updated in time. Using the stale DNS data will most likely cause problems for users, so it is important to keep this is mind.

### 2-Node Clusters
Using failover with a 2-node cluster is not beneficial because during the failover process involving one writer node and one reader node, the two nodes simply switch roles; the reader becomes the writer and the writer becomes the reader. If failover is triggered because one of the nodes has a problem, this problem will persist because there aren't any extra nodes to take the responsibility of the one that is broken. Three or more database nodes are recommended to improve the stability of the cluster.
Expand Down
179 changes: 179 additions & 0 deletions examples/SpringHibernateExample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# Tutorial: Getting Started with the AWS Advanced JDBC Driver, Spring Boot and Hibernate

In this tutorial, you will set up a Spring Boot and Hibernate application with the AWS Advanced JDBC Driver, and use the IAM Authentication plugin to fetch some data from an Aurora PostgreSQL database.

> Note: this tutorial was written using the following technologies:
> - Spring Boot 2.7.1
> - Hibernate
> - AWS Advanced JDBC Driver 2.2.0
> - Postgresql 42.5.4
> - Gradle 7
> - Java 11

You will progress through the following sections:
1. Create a Gradle Spring Boot project
2. Add the required Gradle dependencies
3. Configure the AWS Advanced JDBC Driver

## Pre-requisites
- A database with IAM authentication enabled. This tutorial uses the Aurora PostgreSQL database. For information on how to enable IAM database authentication for Aurora databases, please see the [AWS documentation](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html).

## Step 1: Create a Gradle Project
Create a Gradle project with the following project hierarchy:

```
└───src
├───main
├───java
│ └───example
| ├───data
| ├───Example.java
| └───ExampleRepository.java
│ └───SpringHibernateExampleApplication.java
└───resources
└───application.yml
```

> Note: this sample code assumes the target database contains a table named Example that can be generated using the SQL queries provided in `src/main/resources/example.sql`.

`SpringHibernateExampleApplication.java` contains the following the code:

```java
@SpringBootApplication
public class SpringHibernateExampleApplication implements CommandLineRunner {
private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

@Autowired
ExampleRepository repository;

@Override
public void run(String... args) {
LOGGER.info("Example -> {}", repository.findAll());
}

public static void main(String[] args) {
SpringApplication.run(SpringHibernateExampleApplication.class, args);
}
}
```

`Example.java` contains the following code:

```java
package example.data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Example {

@Id
@GeneratedValue
private int id;

private int status;

public Example() {
super();
}

public Example(int id, int status) {
super();
this.id = id;
this.status = status;
}

public Example(int status) {
super();
this.status = status;
}

public int getId() {
return id;
}

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

public int getStatus() {
return status;
}

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

@Override
public String toString() {
return String.format("Example [id=%s, status=%s]", id, status);
}
}
```
Lastly, `ExampleRepository.java` contains the following code:

```java
package example.data;

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

public interface ExampleRepository extends JpaRepository<Example, Integer> {

}
```

## Step 2: Add the required Gradle Dependencies
In your `build.gradle.kts`, add the following dependencies.

```
dependencies {
implementation("org.springframework.boot:spring-boot-starter-jdbc")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.postgresql:postgresql")
implementation("software.amazon.jdbc:aws-advanced-jdbc-wrapper")
}
```

## Step 3: Configure Spring and Hibernate
Configure Spring to use the AWS Advanced JDBC Driver as the default datasource.

1. In the `application.yml`, add a new datasource for Spring:
```yaml
datasource:
url: jdbc:aws-wrapper:postgresql://db-identifier.cluster-XYZ.us-east-2.rds.amazonaws.com:5432/db
username: jane_doe
driver-class-name: software.amazon.jdbc.Driver
hikari:
data-source-properties:
wrapperPlugins: iam,failover,efm
iamRegion: us-east-2
iamExpiration: 1320
exception-override-class-name: software.amazon.jdbc.util.HikariCPSQLException
max-lifetime: 1260000
```
Since Spring 2+ uses Hikari to manage datasources, to configure the driver we would need to specify the `data-source-properties` under `hikari`.
Whenever Hikari is used, we also need to ensure failover exceptions are handled correctly so connections will not be discarded from the pool after failover has occurred. This can be done by overriding the exception handling class. For more information on this, please see [the documentation on HikariCP](../../docs/using-the-jdbc-driver/using-plugins/UsingTheFailoverPlugin.md#hikaricp).

This example contains some very simple configurations for the IAM Authentication plugin, if you are interested in other configurations related to failover, please visit [the documentation for failover parameters](../../docs/using-the-jdbc-driver/using-plugins/UsingTheFailoverPlugin.md#failover-parameters)
2. Configure Hibernate dialect:
```properties
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this block labeled a properties block instead of a yaml block?

jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
```
3. [Optional] You can enable driver logging by adding the following to `application.yml`:
```yaml
logging:
level:
software:
amazon:
jdbc: TRACE
```

Start the application by running `./gradlew :springhibernate:bootRun` in the terminal. You should see the application making a connection to the database and fetching data from the Example table.

# Summary
This tutorial walks through the steps required to add and configure the AWS Advanced JDBC Driver to a simple Spring Boot and Hibernate application.
28 changes: 28 additions & 0 deletions examples/SpringHibernateExample/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
id("org.springframework.boot") version "2.7.0"
id("io.spring.dependency-management") version "1.1.0"
}

dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.postgresql:postgresql:42.5.4")
implementation("software.amazon.awssdk:rds:2.20.49")
implementation(project(":aws-advanced-jdbc-wrapper"))
}
16 changes: 16 additions & 0 deletions examples/SpringHibernateExample/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License").
# You may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Do not publish the Jar file for this subproject
nexus.publish=false
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package example;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import example.data.ExampleRepository;

@SpringBootApplication
public class SpringHibernateExampleApplication implements CommandLineRunner {
private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

@Autowired
ExampleRepository repository;

@Override
public void run(String... args) {
LOGGER.info("Example -> {}", repository.findAll());
}

public static void main(String[] args) {
SpringApplication.run(SpringHibernateExampleApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package example.data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Example {

@Id
@GeneratedValue
private int id;

private int status;

public Example() {
super();
}

public Example(int id, int status) {
super();
this.id = id;
this.status = status;
}

public Example(int status) {
super();
this.status = status;
}

public int getId() {
return id;
}

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

public int getStatus() {
return status;
}

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

@Override
public String toString() {
return String.format("Example [id=%s, status=%s]", id, status);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package example.data;

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

public interface ExampleRepository extends JpaRepository<Example, Integer> {

}
Loading