Skip to content

Commit

Permalink
AYS-297 | Application DataSource Has Been Configured for Reader and W…
Browse files Browse the repository at this point in the history
…riter Instances (#364)
  • Loading branch information
agitrubard authored Sep 14, 2024
1 parent eda1813 commit 6f61411
Show file tree
Hide file tree
Showing 18 changed files with 186 additions and 43 deletions.
5 changes: 0 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,6 @@
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>

<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-spatial</artifactId>
</dependency>
<!-- ===================== -->
<!-- database dependencies -->
<!-- ===================== -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
* The class is also annotated with {@code @RequiredArgsConstructor} to automatically generate a constructor based on the declared final fields.
*/
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
class AdminRegistrationApplicationServiceImpl implements AdminRegistrationApplicationService {

Expand Down Expand Up @@ -93,7 +92,6 @@ public AdminRegistrationApplication findSummaryById(String id) {
* @return A response object containing the created register application.
*/
@Override
@Transactional
public AdminRegistrationApplication create(AdminRegistrationApplicationCreateRequest request) {

boolean isInstitutionExists = institutionReadPort.existsByIdAndIsStatusActive(request.getInstitutionId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ class AdminRegistrationCompleteServiceImpl implements AdminRegistrationCompleteS
* @param completeRequest The request containing necessary information to complete the registration.
* This includes user details such as email, phone number, and password.
* @throws AdminRegistrationApplicationNotExistException if the registration application does not exist or is not in a waiting state.
* @throws AysUserAlreadyExistsByEmailAddressException if a user with the given email already exists.
* @throws AysUserAlreadyExistsByPhoneNumberException if a user with the given phone number already exists.
* @throws AysUserAlreadyExistsByEmailAddressException if a user with the given email already exists.
* @throws AysUserAlreadyExistsByPhoneNumberException if a user with the given phone number already exists.
*/
@Override
public void complete(final String id, final AdminRegistrationApplicationCompleteRequest completeRequest) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import org.ays.auth.util.exception.AysUserNotActiveException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;
Expand All @@ -39,7 +38,6 @@
*/
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
class AysAuthServiceImpl implements AysAuthService {

private final AysUserReadPort userReadPort;
Expand Down Expand Up @@ -69,7 +67,6 @@ class AysAuthServiceImpl implements AysAuthService {
* @throws AysUserDoesNotAccessPageException If the user does not have permission to access the requested page.
*/
@Override
@Transactional
public AysToken authenticate(final AysLoginRequest loginRequest) {

final AysUser user = userReadPort.findByEmailAddress(loginRequest.getEmailAddress())
Expand Down Expand Up @@ -129,7 +126,6 @@ private void validateUserSourcePagePermission(final AysUser user,
* @throws AysUserNotActiveException If the user associated with the refresh token is not active.
*/
@Override
@Transactional
public AysToken refreshAccessToken(final String refreshToken) {

tokenService.verifyAndValidate(refreshToken);
Expand Down Expand Up @@ -174,7 +170,6 @@ private void validateUserStatus(final AysUser user) {
* @param refreshToken the refresh token used to invalidate the associated access token and refresh token
*/
@Override
@Transactional
public void invalidateTokens(final String refreshToken) {

tokenService.verifyAndValidate(refreshToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import org.ays.auth.util.exception.AysUserNotSuperAdminException;
import org.ays.institution.model.Institution;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Set;
Expand All @@ -28,7 +27,6 @@
* </p>
*/
@Service
@Transactional
@RequiredArgsConstructor
class AysRoleCreateServiceImpl implements AysRoleCreateService {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import org.ays.common.model.AysPage;
import org.ays.common.model.AysPageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;
Expand All @@ -24,7 +23,6 @@
* </p>
*/
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
class AysRoleReadServiceImpl implements AysRoleReadService {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import org.ays.auth.util.exception.AysRoleNotExistByIdException;
import org.ays.auth.util.exception.AysUserNotSuperAdminException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Set;
Expand All @@ -29,7 +28,6 @@
* ensuring permissions and role name uniqueness are validated before saving.
*/
@Service
@Transactional
@RequiredArgsConstructor
class AysRoleUpdateServiceImpl implements AysRoleUpdateService {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import org.ays.common.util.AysRandomUtil;
import org.ays.institution.model.Institution;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Set;
Expand All @@ -34,7 +33,6 @@
* </p>
*/
@Service
@Transactional
@RequiredArgsConstructor
class AysUserCreateServiceImpl implements AysUserCreateService {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import org.ays.parameter.model.AysParameter;
import org.ays.parameter.port.AysParameterReadPort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
Expand All @@ -25,7 +24,6 @@
*/
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
class AysUserMailServiceImpl implements AysUserMailService {

private final AysMailService mailService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import org.ays.common.util.AysRandomUtil;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Optional;
Expand All @@ -28,7 +27,6 @@
* </p>
*/
@Service
@Transactional
@RequiredArgsConstructor
class AysUserPasswordServiceImpl implements AysUserPasswordService {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import org.ays.common.model.AysPage;
import org.ays.common.model.AysPageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

Expand All @@ -24,7 +23,6 @@
*/
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
class AysUserReadServiceImpl implements AysUserReadService {

private final AysUserReadPort userReadPort;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import org.ays.auth.util.exception.AysUserNotPassiveException;
import org.ays.common.model.AysPhoneNumber;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Set;
Expand All @@ -30,7 +29,6 @@
* This service handles the update operation of existing users based on the provided update request.
*/
@Service
@Transactional
@RequiredArgsConstructor
class AysUserUpdateServiceImpl implements AysUserUpdateService {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package org.ays.common.config.datasource;

import com.zaxxer.hikari.HikariDataSource;
import jakarta.persistence.EntityManagerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@Configuration
class AysDatabaseConfiguration {

@Value("${spring.datasource.reader.url}")
private String readerUrl;

@Value("${spring.datasource.writer.url}")
private String writerUrl;

@Value("${spring.datasource.username}")
private String username;

@Value("${spring.datasource.password}")
private String password;

@Value("${spring.datasource.driver-class-name}")
private String driverClassName;


@Bean
DataSource readOnlyDataSource() {
return DataSourceBuilder.create()
.username(this.username)
.password(this.password)
.url(this.readerUrl)
.driverClassName(this.driverClassName)
.build();
}

@Bean
@LiquibaseDataSource
DataSource readWriteDataSource() {
return DataSourceBuilder.create()
.username(this.username)
.password(this.password)
.url(this.writerUrl)
.driverClassName(this.driverClassName)
.build();
}

@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
DataSource hikariDataSource() {
return DataSourceBuilder.create()
.type(HikariDataSource.class)
.build();
}

@Bean
@Primary
DataSource routingDataSource(DataSource readWriteDataSource, DataSource readOnlyDataSource) {
return new AysTransactionRoutingDataSource(readWriteDataSource, readOnlyDataSource);
}


@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource readWriteDataSource,
DataSource readOnlyDataSource,
EntityManagerFactoryBuilder entityManagerFactoryBuilder) {

DataSource dataSource = routingDataSource(readWriteDataSource, readOnlyDataSource);
return entityManagerFactoryBuilder
.dataSource(dataSource)
.packages("org.ays")
.build();
}

@Bean
@Primary
PlatformTransactionManager transactionManager(PlatformTransactionManager jpaTransactionManager) {
return new AysTransactionManager(jpaTransactionManager);
}

@Bean
PlatformTransactionManager jpaTransactionManager(EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.ays.common.config.datasource;

import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.hibernate.TransactionException;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;

@RequiredArgsConstructor
class AysTransactionManager implements PlatformTransactionManager {

private final PlatformTransactionManager platformTransactionManager;

@Override
public @NotNull TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
boolean isReadOnly = definition != null && definition.isReadOnly();
AysTransactionRoutingDataSource.setReadonlyDataSource(isReadOnly);
return platformTransactionManager.getTransaction(definition);
}

@Override
public void commit(@NotNull TransactionStatus status) throws TransactionException {
platformTransactionManager.commit(status);
}

@Override
public void rollback(@NotNull TransactionStatus status) throws TransactionException {
platformTransactionManager.rollback(status);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.ays.common.config.datasource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.util.Map;

class AysTransactionRoutingDataSource extends AbstractRoutingDataSource {

private static DataSourceType currentDataSource = DataSourceType.READ_WRITE;

public AysTransactionRoutingDataSource(DataSource readWriteDataSource, DataSource readOnlyDataSource) {

super.setTargetDataSources(
Map.of(
DataSourceType.READ_WRITE, readWriteDataSource,
DataSourceType.READ_ONLY, readOnlyDataSource
)
);

super.setDefaultTargetDataSource(readWriteDataSource);
}

static void setReadonlyDataSource(boolean isReadonly) {

if (isReadonly) {
currentDataSource = DataSourceType.READ_ONLY;
return;
}

currentDataSource = DataSourceType.READ_WRITE;
}

@Override
public Object determineCurrentLookupKey() {
return currentDataSource;
}

private enum DataSourceType {
READ_ONLY,
READ_WRITE
}

}
Loading

0 comments on commit 6f61411

Please sign in to comment.