I have a Spring Boot Gradle project using Spring Data MongoDB.
In order to generate the query type classes, I have to run the gradle build task.
Is there a way for creating the query type classes when saving the file in IntelliJ? I have already enabled annotation processing in the project.
I would like to have a behavior like the lombok plugin, where it is not needed to build the project to see the changes.
Below is my build.gradle file:
buildscript {
ext {
springBootVersion = '2.0.0.RELEASE'
}
repositories {
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath("io.franzbecker:gradle-lombok:1.11")
classpath("gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.9")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: "io.franzbecker.gradle-lombok"
apply plugin: "com.ewerk.gradle.plugins.querydsl"
group = 'com.henrique'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-mongodb')
compile("com.querydsl:querydsl-mongodb:4.1.4")
compileOnly("com.querydsl:querydsl-apt:4.1.4")
testCompile('org.springframework.boot:spring-boot-starter-test')
}
querydsl {
springDataMongo = true
querydslSourcesDir = "$buildDir/generated/source/app/main"
}
sourceSets {
main {
java {
srcDir "$buildDir/generated/source/app/main"
}
}
}
Here is my domain entity:
#Data
#Builder
#Document
public class Customer {
#Id
private String id;
private String firstName;
private String lastName;
private String zipCode;
}
And here is the query type generated:
#Generated("com.querydsl.codegen.EntitySerializer")
public class QCustomer extends EntityPathBase<Customer> {
private static final long serialVersionUID = -1386833698L;
public static final QCustomer customer = new QCustomer("customer");
public final StringPath firstName = createString("firstName");
public final StringPath id = createString("id");
public final StringPath lastName = createString("lastName");
public final StringPath zipCode = createString("zipCode");
public QCustomer(String variable) {
super(Customer.class, forVariable(variable));
}
public QCustomer(Path<? extends Customer> path) {
super(path.getType(), path.getMetadata());
}
public QCustomer(PathMetadata metadata) {
super(Customer.class, metadata);
}
}
Related
I am writing a test explanation, of which I have done this plenty of times, and I am getting strange behavior. I run the test and then I get:
No username
com.bsb.pr.test.domain.UserException: No username
at app//com.bsb.pr.test.service.UserService.fetchUserById(UserService.kt:21)
at app//com.bsb.pr.test.UserServiceTestNoKSpock.getUserNameById throws exception if username is null(UserServiceTestNoKSpock.kt:23)
at java.base#17.0.4/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
The follow is the code that is used:
#Service
class SubscriptionService {
fun getSubscriptionByUserId(id: Number): Subscription? {
return Subscription("email#email.com")
}
}
#Service
class UsernameService {
fun getUserNameById(id: Number): Username? = Username("test")
}
Focal Service
#Service
class UserService(
private val usernameService: UsernameService,
private val subscriptionService: SubscriptionService
) {
fun fetchUserById(id: Number): User {
val username: Username =
usernameService
.getUserNameById(id)
?: throw UserException("No username")
val subscription: Subscription =
subscriptionService
.getSubscriptionByUserId(id)
?: throw UserException("No subscription")
return User(id, username.content, subscription)
}
}
Focal test
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.mockito.Mockito
class UserServiceTest {
private val usernameService: UsernameService = Mockito.mock(UsernameService::class.java)
private val subscriptionService: SubscriptionService = Mockito.mock(SubscriptionService::class.java)
private val userService = UserService(usernameService, subscriptionService)
#Test
fun `getUserNameById throws exception if username is null`() {
// setup mocks
Mockito.`when`(userService.fetchUserById(1)).thenReturn(null)
// whenever
val userException: UserException = assertThrows { userService.fetchUserById(1) }
assert(userException.message == "No username")
Mockito.verify(userService).fetchUserById(1)
}
}
build.gradle
plugins {
id("org.springframework.boot") version "2.7.5"
id("io.spring.dependency-management") version "1.0.15.RELEASE"
kotlin("jvm") version "1.6.21"
kotlin("plugin.spring") version "1.6.21"
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_17
configurations {
compileOnly {
extendsFrom(configurations.annotationProcessor.get())
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.mockito.kotlin:mockito-kotlin:4.0.0")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
I have tried different forms of that assertThrows and looked at other online documentation.
It should notice the exception and then pass.
I am trying to generate the Swagger documentation and thin client for Java from the existing controllers that we have. Following are the sample classes:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "clazz")
#JsonSubTypes({
#JsonSubTypes.Type(value = Cat.class, name = "Cat"),
#JsonSubTypes.Type(value = Fish.class, name = "Fish"),
#JsonSubTypes.Type(value = Frog.class, name = "Frog")
})
public interface Animal {
String getName();
int getType();
}
Here is the controller
#RestController
#RequestMapping("/animals")
public class AnimalController {
#GetMapping(value = "/")
public Animal[] getAnimalsByType(#RequestParam(required = false) String animalType) {
AnimalType type = StringUtils.isNotBlank(animalType) ? AnimalType.valueOf(animalType) : null;
Reflections reflections = new Reflections("com.xyz.openapi.server.model");
return reflections.getSubTypesOf(Animal.class).stream()
.map(animal -> {
try {
return animal.getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException |
NoSuchMethodException | InvocationTargetException e) {
throw new RuntimeException(e);
}
})
.filter(animal -> animalType == null || type.index() == animal.getType())
.toArray(Animal[] :: new);
}
}
Here is the client code generation task from build.gradle
plugins {
id "java"
id "org.springframework.boot" version "2.3.1.RELEASE"
id "io.spring.dependency-management" version "1.0.9.RELEASE"
id "org.openapi.generator" version "5.1.0"
}
openApiGenerate {
inputSpec.set("$rootDir/api-docs.json")
outputDir.set("$rootDir/thinclient")
groupId.set("com.xyz.openapi")
id.set("openapi-thinclient")
version.set("0.0.1-SNAPSHOT")
apiPackage.set("com.xyz.openapi.thinclient.api")
invokerPackage.set("com.xyz.openapi.thinclient.invoker")
modelPackage.set("com.xyz.openapi.thinclient.model")
generatorName.set("java");
library.set("resttemplate")
configOptions.put("serializationLibrary", "jackson")
}
When I generate client code for java using gradle openApiGenerate, I am getting the client API generated as follows
#javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2021-05-24T19:20:33.823042600+05:30[Asia/Calcutta]")
#Component("com.xyz.openapi.thinclient.api.AnimalControllerApi")
public class AnimalControllerApi {
private ApiClient apiClient;
public AnimalControllerApi() {
this(new ApiClient());
}
#Autowired
public AnimalControllerApi(ApiClient apiClient) {
this.apiClient = apiClient;
}
public ApiClient getApiClient() {
return apiClient;
}
public void setApiClient(ApiClient apiClient) {
this.apiClient = apiClient;
}
/**
*
*
* <p><b>200</b> - OK
* #param animalType (optional)
* #return List<OneOfCatFishFrog>
* #throws RestClientException if an error occurs while attempting to invoke the API
*/
public **List<OneOfCatFishFrog>** getAnimalsByType(String animalType) throws RestClientException {
return getAnimalsByTypeWithHttpInfo(animalType).getBody();
}
}
The return type of the API is generated as List<OneOfCatFishFrog> instead of List<Animal>. Is there any change that I need to do to get this done? Please suggest!
I have a problem when starting spring-boot appication with Using JpaRepository.
more details, deleting findReviewByUser_num inside ReviewRepository works well.
My code did not have a Controller, Service, and I was creating Repository and just testing.
Review.Java
package org.soma.tripper.review.entity;
import lombok.*;
import javax.persistence.*;
import java.util.List;
#Getter
#Entity
#NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Review {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int review_num;
#Column(name = "user_num")
private int user_num;
#Column(name = "schedule_num")
private String schedule_num;
#Column(name = "content",length = 1000)
private String content;
#Column(name="rating")
private double rating;
#Column(name="ml_rating")
private double ml_rating;
#Builder Review(int user_num, int schedule_num, String content, double rating){
this.user_num=user_num;
this.user_num = schedule_num;
this.content=content;
this.rating = rating;
}
}
ReviewRepository.java
package org.soma.tripper.review.repository;
import org.soma.tripper.review.entity.Review;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ReviewRepository extends JpaRepository<Review,Integer> {
Review findReviewByUser_num(int user_num);
}
build.gradle
buildscript {
ext {
springBootVersion = '2.0.3.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
jar {
manifest {
attributes 'Main-Class': 'org.soma.tripper.TripperApplication'
}
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
group = 'org.soma'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
//swagger
compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.5.0'
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.5.0'
//jpa
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compileOnly('org.projectlombok:lombok')
//logback
compile('ch.qos.logback:logback-classic:1.2.3')
compile('ch.qos.logback:logback-core:1.2.3')
compile 'log4j:log4j:1.2.17'
compile 'org.slf4j:slf4j-api:1.7.5'
compile 'org.slf4j:slf4j-log4j12:1.7.5'
// S3
// https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk
compile group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.11.371'
// https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.6'
compile('mysql:mysql-connector-java')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
console
I'm trying to build a simple api with spring boot and kotlin. When I make a request to save a new user everything goes well, a new user is persisted, but response body is empty. I don't know why, I return the new user. Please help me to understand why Jackson isn't serializing my kotlin class.
I can assure that the user returned is not null.
Entity:
#Entity
data class User(#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private val id: Long = 0L,
private var createdAt: LocalDateTime = LocalDateTime.now(),
private var enabled: Boolean = false,
private val username: String = "",
private val password: String = "",
private val name: String = "") {
#PrePersist
private fun onCreate() {
this.enabled = true
this.createdAt = LocalDateTime.now();
}
}
Controller:
#RestController
#RequestMapping("users")
class UserController {
#Autowired
private lateinit var userService: UserService
#PostMapping
fun save(#RequestBody user: User): User {
return this.userService.save(user)
}
}
build.gradle:
buildscript {
ext {
kotlinVersion = '1.2.20'
springBootVersion = '2.0.0.RC1'
}
repositories {
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
}
}
apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'br.com'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
compileKotlin {
kotlinOptions {
freeCompilerArgs = ["-Xjsr305=strict"]
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
freeCompilerArgs = ["-Xjsr305=strict"]
jvmTarget = "1.8"
}
}
repositories {
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-web')
compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
compile("org.jetbrains.kotlin:kotlin-reflect")
//JACKSON
compile "com.fasterxml.jackson.core:jackson-annotations"
compile "com.fasterxml.jackson.core:jackson-core"
compile "com.fasterxml.jackson.core:jackson-databind"
runtime "com.fasterxml.jackson.datatype:jackson-datatype-jdk8"
runtime "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"
runtime "com.fasterxml.jackson.module:jackson-module-kotlin"
runtime('mysql:mysql-connector-java')
runtime('org.springframework.boot:spring-boot-devtools')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
application.properties
#DATA SOURCE
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc\:mysql\://localhost:3307/kotlin-library
spring.datasource.username = root
spring.datasource.password =
#JPA
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
#SERVER
server.servlet.context-path=/
#JACKSON
spring.jackson.serialization.indent-output=true
spring.jackson.serialization.write-dates-as-timestamps=false
spring.jackson.serialization.write-durations-as-timestamps=false
Everything in your data class is private. Typically serialization ignores private members in a class. If you make the necessary fields you desire to get serialized public, or just remove the private keyword I'm betting it works for you.
I am writing a custom plugin for Gradle. I want to be able to have:
serviceDependencies {
service name: 'service1', version: '1.0'
service name: 'service2', version: '1.1'
}
In my Plugin implementation (in Java) I have:
public void apply(final Project project) {
project.getExtensions().create("serviceDependencies", Services.class);
project.getExtensions().create("service", Service.class);
}
And Service.java:
public class Service {
private String name;
private String version;
public Service(final String name, final String version) {
this.name = name;
this.version = version;
}
public String getName() {
return this.name;
}
public void setName(final String name) {
this.name = name;
}
public String getVersion() {
return this.version;
}
public void setVersion(final String version) {
this.version = version;
}
}
When I try use this plugin I get:
java.lang.IllegalArgumentException: Could not find any public constructor for class com.xxx.xxx.Service_Decorated which accepts parameters [].
This still happens when I remove serviceDependencies/Services.java from the picture.
If I remove the Service constructor or remove the arguments.
org.gradle.internal.metaobject.AbstractDynamicObject$CustomMessageMissingMethodException: Could not find method service() for arguments [{name=service1, version=1.0}] on root project ...
Obviously my pojo is being decorated, but not quite with the correct constructor. How can I get the constructor to work how I want in my build.gradle script?
A second and independent question is what should Services.java look like?
I would only register one extension for serviceDependencies {} that exposes functions to register your services:
public class Services {
void service(String name, String version) { /* new Service(...) */ }
}
project.getExtensions().create("serviceDependencies", Services.class);
This would allow Java, Kotlin and Groovy consumer to do something like:
serviceDependencies {
service 'service1', '1.0'
service 'service2', '0.1'
}
Then if you want to support Groovy named arguments you'd need to add:
public class Services {
void service(String name, String version) { /* new Service(...) */ }
void service(Map<String, String> namedArguments) {
service(namedArguments.get("name"), namedArguments.get("version"))
}
}
This would allow Groovy consumers to do:
serviceDependencies {
service name: 'service1', version: '1.0'
service name: 'service2', version: '0.1'
}