Add basic module and redis data with jpa and example
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
bc81cb0988
commit
8c999a9e7c
154
README.md
154
README.md
@ -1,16 +1,170 @@
|
||||
# CUBETIQ Web Modules (Template)
|
||||
|
||||
- Setup and Default Web Configuration
|
||||
- Swagger UI and API's Documentation (SpringFox)
|
||||
- General Purpose for External and Internal use-cases
|
||||
- Dockerfile and Docker profile build support
|
||||
|
||||
# Modules
|
||||
|
||||
- API (Default Module)
|
||||
|
||||
### Spring Data
|
||||
|
||||
- Add `spring-data-jpa` dependency in `build.gradle.kts`
|
||||
|
||||
```kotlin
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||
```
|
||||
|
||||
- Add `spring-data-redis` dependency in `build.gradle.kts` (Redis + Driver)
|
||||
|
||||
```kotlin
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-redis")
|
||||
```
|
||||
|
||||
### Spring Boot Properties
|
||||
|
||||
- Recommend
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
application:
|
||||
name: ${APP_NAME:spring-web-api}
|
||||
app:
|
||||
data-dir: ${APP_DATA_DIR:${user.home}/${spring.application.name}}
|
||||
```
|
||||
|
||||
- Upload File Properties
|
||||
|
||||
```yaml
|
||||
server:
|
||||
tomcat:
|
||||
max-http-form-post-size: ${SERVER_MAX_HTTP_FORM_POST_SIZE:50MB}
|
||||
|
||||
spring:
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 256MB
|
||||
max-request-size: 256MB
|
||||
enabled: true
|
||||
```
|
||||
|
||||
- Logging
|
||||
|
||||
```yaml
|
||||
logging:
|
||||
file:
|
||||
path: ${LOGGING_FILE_PATH:${app.data-dir}/logs/}
|
||||
name: ${logging.file.path}/app.log
|
||||
```
|
||||
|
||||
### Spring Data Redis
|
||||
|
||||
- Redis Properties
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
redis:
|
||||
host: ${REDIS_HOST:localhost}
|
||||
port: ${REDIS_PORT:6379}
|
||||
password: ${REDIS_PASSWORD:your_password}
|
||||
```
|
||||
|
||||
### Spring Data JPA Properties
|
||||
|
||||
- Datasource Enhancement (Default: PostgresSQL)
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
datasource:
|
||||
driverClassName: ${DATA_SOURCE_DRIVER_CLASS_NAME:org.postgresql.Driver}
|
||||
url: ${DATA_SOURCE_URL:jdbc:postgresql://localhost:5432/demo}
|
||||
username: ${POSTGRES_USERNAME:postgres}
|
||||
password: ${POSTGRES_PASSWORD:postgres}
|
||||
hikari:
|
||||
max-lifetime: ${DATA_SOURCE_MAX_LIFETIME:1800000}
|
||||
connection-timeout: ${DATA_SOURCE_CONNECTION_TIMEOUT:30000}
|
||||
idle-timeout: ${DATA_SOURCE_IDLE_TIMEOUT:600000}
|
||||
maximum-pool-size: ${DATA_SOURCE_MAXIMUM_POOL_SIZE:10}
|
||||
allow-pool-suspension: ${DATA_SOURCE_ALLOW_POOL_SUSPENSION:true}
|
||||
tomcat:
|
||||
max_active: ${DATA_SOURCE_TOMCAT_MAX_ACTIVE:100}
|
||||
max_idle: ${DATA_SOURCE_TOMCAT_MAX_IDLE:10}
|
||||
min-idle: ${DATA_SOURCE_TOMCAT_MIN_IDLE:10}
|
||||
initial_size: ${DATA_SOURCE_TOMCAT_INITIAL_SIZE:10}
|
||||
remove_abandoned: ${DATA_SOURCE_TOMCAT_REMOVE_ABANDONED:true}
|
||||
jpa:
|
||||
database-platform: ${JPA_DATABASE_PLATFORM:org.hibernate.dialect.PostgreSQLDialect}
|
||||
show-sql: ${JPA_SHOW_SQL:false}
|
||||
hibernate:
|
||||
ddl-auto: ${JPA_HIBERNATE_DDL_AUTO:update}
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: ${JPA_HIBERNATE_DIALECT:org.hibernate.dialect.PostgreSQLDialect}
|
||||
open-in-view: ${JPA_OPEN_IN_VIEW:false}
|
||||
```
|
||||
|
||||
- PostgreSQL
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
datasource:
|
||||
driverClassName: ${DATA_SOURCE_DRIVER_CLASS_NAME:org.postgresql.Driver}
|
||||
url: jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT:5432}/${POSTGRES_DB:demo}
|
||||
username: ${POSTGRES_USERNAME:postgres}
|
||||
password: ${POSTGRES_PASSWORD:postgres}
|
||||
```
|
||||
|
||||
- MySQL
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
datasource:
|
||||
driverClassName: ${DATA_SOURCE_DRIVER_CLASS_NAME:com.mysql.cj.jdbc.Driver}
|
||||
url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/${MYSQL_DB:demo}?createDatabaseIfNotExist=true&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
|
||||
username: ${MYSQL_USERNAME:demo}
|
||||
password: ${MYSQL_PASSWORD:demo}
|
||||
jpa:
|
||||
database-platform: ${JPA_DATABASE_PLATFORM:org.hibernate.dialect.MySQLDialect}
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: ${JPA_HIBERNATE_DIALECT:org.hibernate.dialect.MySQLDialect}
|
||||
```
|
||||
|
||||
- H2 (Embedded)
|
||||
-
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
datasource:
|
||||
driverClassName: ${DATA_SOURCE_DRIVER_CLASS_NAME:org.h2.Driver}
|
||||
url: jdbc:h2:file:${H2_DB_PATH:./data/db};DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
||||
username: ${H2_USERNAME:sa}
|
||||
password: ${H2_PASSWORD:password}
|
||||
jpa:
|
||||
database-platform: ${JPA_DATABASE_PLATFORM:org.hibernate.dialect.H2Dialect}
|
||||
h2:
|
||||
console:
|
||||
enabled: ${H2_CONSOLE_ENABLED:true}
|
||||
```
|
||||
|
||||
- Avoid the Lazy Initialization Problem
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
jpa:
|
||||
properties:
|
||||
hibernate:
|
||||
enable_lazy_load_no_trans: ${HIBERNATE_LAZY_NO_TRANS:true}
|
||||
```
|
||||
|
||||
# Contributors
|
||||
|
||||
- Sambo Chea <sombochea@cubetiqs.com>
|
||||
|
||||
### Language and Framework
|
||||
|
||||
- Spring Boot: 2.6.6
|
||||
- Kotlin: 1.6.20
|
||||
- Gradle: 7.4.1
|
||||
|
@ -5,6 +5,7 @@ plugins {
|
||||
id("io.spring.dependency-management")
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.spring")
|
||||
kotlin("plugin.jpa")
|
||||
}
|
||||
|
||||
val kotlinVersion = "1.6.20"
|
||||
@ -32,6 +33,10 @@ springBoot {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Spring Data JPA (Required for Database Layer)
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-redis")
|
||||
|
||||
// Migrating from SpringFox
|
||||
implementation("org.springdoc:springdoc-openapi-ui:1.6.7")
|
||||
|
||||
@ -44,6 +49,10 @@ dependencies {
|
||||
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
|
||||
runtimeOnly("com.h2database:h2")
|
||||
runtimeOnly("org.postgresql:postgresql")
|
||||
|
||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,20 @@
|
||||
package com.cubetiqs.web.modules.redis
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory
|
||||
import org.springframework.data.redis.core.RedisTemplate
|
||||
|
||||
@RedisModule
|
||||
@Configuration
|
||||
class RedisConfig @Autowired constructor(
|
||||
private val connectionFactory: RedisConnectionFactory,
|
||||
) {
|
||||
@Bean
|
||||
fun redisTemplate(): RedisTemplate<String, RedisKVModel> {
|
||||
val template = RedisTemplate<String, RedisKVModel>()
|
||||
template.setConnectionFactory(connectionFactory)
|
||||
return template
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.cubetiqs.web.modules.redis
|
||||
|
||||
import com.cubetiqs.web.util.RouteConstants
|
||||
import io.swagger.v3.oas.annotations.tags.Tag
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.data.redis.core.RedisTemplate
|
||||
import org.springframework.web.bind.annotation.*
|
||||
|
||||
@RedisModule
|
||||
@Tag(name = "Redis Controller")
|
||||
@RestController
|
||||
@RequestMapping(RouteConstants.INDEX + "redis")
|
||||
class RedisController @Autowired constructor(
|
||||
private val redisTemplate: RedisTemplate<String, RedisKVModel>,
|
||||
) {
|
||||
@GetMapping("/{key}")
|
||||
fun getAll(
|
||||
@PathVariable("key") key: String,
|
||||
): Collection<RedisKVModel?> {
|
||||
return redisTemplate.opsForValue().multiGet(listOf(key)) ?: listOf()
|
||||
}
|
||||
|
||||
@PostMapping("/{key}")
|
||||
fun set(
|
||||
@PathVariable("key") key: String,
|
||||
@RequestBody body: RedisKVModel
|
||||
): RedisKVModel {
|
||||
redisTemplate.opsForValue().set(key, body)
|
||||
return body
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.cubetiqs.web.modules.redis
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
data class RedisKVModel(
|
||||
var key: String? = null,
|
||||
var value: Any? = null,
|
||||
) : Serializable {
|
||||
companion object {
|
||||
fun create(key: String, value: Any): RedisKVModel {
|
||||
return RedisKVModel(key, value)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.cubetiqs.web.modules.redis
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
|
||||
@ConditionalOnProperty(name = ["spring.redis.enabled"], havingValue = "true")
|
||||
annotation class RedisModule
|
@ -0,0 +1,60 @@
|
||||
package com.cubetiqs.web.modules.user
|
||||
|
||||
import com.cubetiqs.web.util.RouteConstants
|
||||
import io.swagger.v3.oas.annotations.Parameter
|
||||
import io.swagger.v3.oas.annotations.tags.Tag
|
||||
import org.springdoc.core.converters.models.PageableAsQueryParam
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.data.domain.Page
|
||||
import org.springframework.data.domain.Pageable
|
||||
import org.springframework.web.bind.annotation.*
|
||||
import java.util.*
|
||||
|
||||
@UserModule
|
||||
@Tag(name = "User Controller")
|
||||
@RestController
|
||||
@RequestMapping(RouteConstants.INDEX + "user")
|
||||
class UserController @Autowired constructor(
|
||||
private val userRepository: UserRepository,
|
||||
) {
|
||||
@GetMapping
|
||||
@PageableAsQueryParam
|
||||
fun getAll(
|
||||
@Parameter(hidden = true)
|
||||
pageable: Pageable?,
|
||||
): Page<UserEntity> {
|
||||
return userRepository.findAll(pageable ?: Pageable.unpaged())
|
||||
}
|
||||
|
||||
@ResponseStatus(value = org.springframework.http.HttpStatus.CREATED)
|
||||
@PostMapping
|
||||
fun create(
|
||||
@RequestBody body: UserEntity
|
||||
): UserEntity {
|
||||
return userRepository.save(body)
|
||||
}
|
||||
|
||||
@ResponseStatus(value = org.springframework.http.HttpStatus.OK)
|
||||
@PutMapping("/{id}")
|
||||
fun update(
|
||||
@PathVariable id: String,
|
||||
@RequestBody body: UserEntity
|
||||
): UserEntity {
|
||||
val user = userRepository.findById(UUID.fromString(id)).orElseThrow {
|
||||
throw IllegalArgumentException("User not found")
|
||||
}
|
||||
body.id = user.id
|
||||
return userRepository.save(body)
|
||||
}
|
||||
|
||||
@ResponseStatus(value = org.springframework.http.HttpStatus.NO_CONTENT)
|
||||
@DeleteMapping("/{id}")
|
||||
fun delete(
|
||||
@PathVariable id: String,
|
||||
) {
|
||||
val user = userRepository.findById(UUID.fromString(id)).orElseThrow {
|
||||
throw IllegalArgumentException("User not found")
|
||||
}
|
||||
userRepository.delete(user)
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.cubetiqs.web.modules.user
|
||||
|
||||
import org.hibernate.Hibernate
|
||||
import java.io.Serializable
|
||||
import java.util.*
|
||||
import javax.persistence.*
|
||||
|
||||
@Entity
|
||||
@Table(name = "user")
|
||||
open class UserEntity(
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
open var id: UUID? = null,
|
||||
|
||||
@Column(name = "name", length = 50)
|
||||
open var name: String? = null,
|
||||
|
||||
@Column(name = "username", length = 50, unique = true)
|
||||
open var username: String? = null,
|
||||
) : Serializable {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false
|
||||
other as UserEntity
|
||||
|
||||
return id != null && id == other.id
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = javaClass.hashCode()
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.cubetiqs.web.modules.user
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
|
||||
@ConditionalOnProperty(name = ["module.user.enabled", "spring.datasource.enabled"], havingValue = "true")
|
||||
annotation class UserModule
|
@ -0,0 +1,9 @@
|
||||
package com.cubetiqs.web.modules.user
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.stereotype.Repository
|
||||
import java.util.*
|
||||
|
||||
@UserModule
|
||||
@Repository
|
||||
interface UserRepository : JpaRepository<UserEntity, UUID>
|
@ -1,8 +1,22 @@
|
||||
spring:
|
||||
profiles:
|
||||
active: ${APP_PROFILE:dev}
|
||||
active: ${APP_PROFILE:demo}
|
||||
application:
|
||||
name: cubetiq-api-service
|
||||
redis:
|
||||
enabled: ${REDIS_ENABLED:false}
|
||||
host: ${REDIS_HOST:localhost}
|
||||
password: ${REDIS_PASSWORD:null}
|
||||
datasource:
|
||||
enabled: ${DATASOURCE_ENABLED:false}
|
||||
driverClassName: ${DATA_SOURCE_DRIVER_CLASS_NAME:org.h2.Driver}
|
||||
url: jdbc:h2:file:${H2_DB_PATH:${cubetiq.app.data-dir}/data/db};DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
||||
username: ${H2_USERNAME:sa}
|
||||
password: ${H2_PASSWORD:password}
|
||||
|
||||
module:
|
||||
user:
|
||||
enabled: ${MODULE_USER_ENABLED:false}
|
||||
|
||||
cubetiq:
|
||||
app:
|
||||
|
8
api/src/main/resources/banner.txt
Normal file
8
api/src/main/resources/banner.txt
Normal file
@ -0,0 +1,8 @@
|
||||
----------------------- CUBEIQ @sombochea ---------------------------
|
||||
________ _____ ___ __ ______
|
||||
__ ___/________ ___________(_)_______ _______ _ __ | / /_____ ___ /_
|
||||
_____ \ ___ __ \__ ___/__ / __ __ \__ __ `/ __ | /| / / _ _ \__ __ \
|
||||
____/ / __ /_/ /_ / _ / _ / / /_ /_/ / __ |/ |/ / / __/_ /_/ /
|
||||
/____/ _ .___/ /_/ /_/ /_/ /_/ _\__, / ____/|__/ \___/ /_.___/
|
||||
/_/ /____/
|
||||
Spring Boot Version: ${spring-boot.formatted-version}
|
@ -5,7 +5,7 @@ plugins {
|
||||
id("io.spring.dependency-management") version "1.0.11.RELEASE" apply false
|
||||
kotlin("jvm") version "1.6.20" apply false
|
||||
kotlin("plugin.spring") version "1.6.20" apply false
|
||||
// kotlin("plugin.jpa") version "1.6.10" apply false
|
||||
kotlin("plugin.jpa") version "1.6.20" apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
@ -15,8 +15,13 @@ metadata:
|
||||
namespace: spring-web-dev
|
||||
name: spring-web-dev-secret
|
||||
stringData:
|
||||
REDIS_HOST: redis-service
|
||||
REDIS_PASSWORD: demo
|
||||
POSTGRES_USER: demo
|
||||
POSTGRES_DB: demo
|
||||
POSTGRES_PASSWORD: demo
|
||||
APP_DATA_DIR: /opt/cubetiq/data
|
||||
MODULE_USER_ENABLED: true
|
||||
DATASOURCE_ENABLED: true
|
||||
REDIS_ENABLED: true
|
||||
type: Opaque
|
Loading…
Reference in New Issue
Block a user