R2DBC - IllegalArgumentException: Cannot encode parameter of type java.util.Date - spring-webflux

I'm learning Reative jdbc with R2DBC MySQL. I have a repository like this:
public interface VerificationTokenRepository extends ReactiveCrudRepository<VerificationToken,Long> {
The VerificationToken class looks like this:
#Table
public class VerificationToken {
#Id
private final Long id;
#Column
private final String code;
#Column
private final Date validUntil;
#Column
private boolean used;
#Column
private Long userId;
#Column
private Long verificationTokenType;
The script to create the table is this one:
create table verification_token (
id int unsigned not null AUTO_INCREMENT,
code varchar(36) not null,
valid_until datetime not null,
used boolean not null,
verification_token_type_id int unsigned not null,
user_id int unsigned,
PRIMARY KEY (id),
constraint verification_token_type_fk FOREIGN KEY (verification_token_type_id) REFERENCES verification_token_type(id),
CONSTRAINT user_id_fk FOREIGN KEY (user_id) REFERENCES user(id)
);
When I execute the method verificationTokenRepository.save the console shows this error:
Caused by: java.lang.IllegalArgumentException: Cannot encode parameter of type java.util.Date
at io.r2dbc.h2.codecs.DefaultCodecs.encode(DefaultCodecs.java:73)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ SQL "INSERT INTO verification_token (code, valid_until, used, user_id, verification_token_type) VALUES ($1, $2, $3, $4, $5)" [DatabaseClient]
Stack trace:
POM file
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<!-- For testing possibility -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>dev.miku</groupId>
<artifactId>r2dbc-mysql</artifactId>
<version>0.8.2.RELEASE</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-h2</artifactId>
</dependency>
How can I store a Date using R2DBC? Or if R2DBV support it?

Instead of Date Use Instant from import java.time.Instant package.

please make sure to use LocalDateTime in java 8,
The error will go away.
https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html
According to your code,
#Column
private final LocalDateTime validUntil;

Use Timestamp.
Example:
CREATE TABLE tweet (id SERIAL PRIMARY KEY, tweet VARCHAR(255), created TIMESTAMP, updated TIMESTAMP );
Repository:
package com.reactive.demo.reactivedemo;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
public interface TweetRepository extends ReactiveCrudRepository<Tweet, Long> {
}
Bean:
package com.reactive.demo.reactivedemo;
import org.springframework.data.annotation.Id;
import java.time.Instant;
public class Tweet {
#Id
private Long id;
public Tweet(Long id, Instant created, Instant updated, String tweet) {
this.id = id;
this.created = created;
this.updated = updated;
this.tweet = tweet;
}
public Tweet() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Instant getCreated() {
return created;
}
public void setCreated(Instant created) {
this.created = created;
}
public Instant getUpdated() {
return updated;
}
public void setUpdated(Instant updated) {
this.updated = updated;
}
public String getTweet() {
return tweet;
}
#Override
public String toString() {
return "Tweet{" +
"id=" + id +
", created=" + created +
", updated=" + updated +
", tweet='" + tweet + '\'' +
'}';
}
public void setTweet(String tweet) {
this.tweet = tweet;
}
private Instant created;
private Instant updated;
private String tweet;
}
Testting code
#Bean
public CommandLineRunner demo(TweetRepository tweetRepo){
return (args) -> {
Flux<Tweet> tweetFlux = Flux.just(new Tweet(null, Instant.ofEpochMilli(System.currentTimeMillis()),Instant.ofEpochMilli(System.currentTimeMillis()), "HEllo")).flatMap(tweetRepo::save);
tweetFlux.thenMany((tweetRepo.findAll())).subscribe(System.out::println);
};
}

Related

Why use terminal to start jar and idea to start are inconsistent

Using idea to start the springboot program cannot get the value of the automatic configuration class
For example, the following code,PassConfig.getKey() is null:
private static final String encryptionFormat = String.format("to_base64(aes_encrypt(?,'%s'))", PassConfig.getKey());
#Component
#ConfigurationProperties(prefix = "mybatis.key")
public class PassConfig {
private static String key;
public static String getKey() {
return key;
}
public void setKey(String key) {
PassConfig.key = key;
}
}
At present, it can be solved by org.springframework.beans.factory.annotation.Value annotations.
#Value("${mybatiskey}")
public void setKey(String key) {
PassConfig.key = key;
}
In PassConfig you specified the prefix to be "mybatis.key" and the String as key
According to this, in application.properties your property is expected to be mybatis.key.key (prefix.variablename)
in setKey you specified the #Value to take mybatiskey from application.properties
property names should be consistent. It is null because most probably you don't have the propery mybatis.key.key in your application.properties file.

Field 'id' doesn't have a default value Hibernate SQL

public class Coal {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String brand;
#Column(name = "cube_size")
private double cubeSize;
private String country;
private double price;
Addition new coal via controller
public static final String ALL_COALS = "/coals";
#PostMapping(ALL_COALS + "/add")
public Coal addCoal(#RequestBody Coal coal) {
return coalService.addCoal(coal);
}
Flyway migration script for this table
(
id bigint not null auto_increment,
brand varchar(255),
cube_size double,
country varchar(255),
price double,
primary key (id)
);
And when I try to send POST request with Insomnia http://localhost:8080/coals/add with JSON
{
"brand": "Loop",
"cubeSize": 2.4,
"country": "Russia",
"price": 54
}
I receive the error Field 'id' doesn't have a default value.
I tried to insert JSON with id as well but no changes.
I saw some solutions but I have already added auto_increment to my migration like many people advised.
Update:
#Service
public Coal addCoal(Coal coal) {
return coalRepository.save(coal);
}
And Repo
#Repository
public interface CoalRepository extends CrudRepository<Coal, Long> {

Kotlin get declared member property value

What is the equivalent code of Class#getDeclaredField in Kotlin? I'm looking for a pure Kotlin implementation... a reflection perhaps?
class Test {
public static final String TEST = "Hello";
public static void main(String[] args) {
System.out.println(
Test.class.getDeclaredField("TEST").get(Test.class));
}
}
first, add this dependency to avoid runtime error
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>1.3.50</version>
</dependency>
then:
val member = Test::class.members.find { it.name=="TEST" }
println(member)
println(member.call(Test())
output:
val generic.Test.TEST: kotlin.String
Hello
member type is Kcallable<*>?
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-callable/index.html

How to return custom Key value pair from spring-cloud-config-server?

I am using spring-cloud-config-server , I do not want Git backend or file system based backend . I want custom Key value pair to be returned.
I found solution of this .
1) on the application.properties set spring.profiles.active=native
2) Create CustomEnvironmentRepository -- Refer code on #A
3) Register CustomEnvironmentRepository Bean -- Refer code on #B
#A -
import java.util.HashMap;
import java.util.Map;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
import org.springframework.core.Ordered;
public class CustomEnvironmentRepository implements
EnvironmentRepository, Ordered
{
#Override
public Environment findOne(String application, String profile, String label)
{
Environment environment = new Environment(application, profile);
final Map<String, String> properties = new HashMap<>();
properties.put("mycustomPropertyKey", "myValue");
environment.add(new PropertySource("mapPropertySource", properties));
return environment;
}
#Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
#B
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
import org.springframework.context.annotation.Bean;
#EnableConfigServer
#SpringBootApplication
public class MyCloudConfigApplication {
public static void main(String[] args) {
SpringApplication.run(MyCloudConfigApplication.class, args);
}
#Bean
public EnvironmentRepository environmentRepository() {
return new CustomEnvironmentRepository();
}
}
Test by invoking - http://localhost:9092/my-cloud-config.properties
You will see
mycustomPropertyKey: myValue
My Original Goal is to set some properties from Azure Key Vault , I guess integrating AzureKey Vault in CustomEnvironmentRepository , I should be able to achieve this
Application Properties File will look like
server.port=<YOUR_PORT>
spring.profiles.active=native
azure.keyvault.uri=<YOUR_AZURE_URI_CAN_BE_FOUND_IN_AZURE_PORTAL>
azure.keyvault.client-id=<YOUR_AZURE_CLIENT_ID_CAN_BE_FOUND_IN_AZURE_PORTAL>
azure.keyvault.client-key=<YOUR_AZURE_CLIENT_KEY_CAN_BE_FOUND_IN_AZURE_PORTAL>
IN POM Use these Dependencies
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-keyvault-secrets-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>

Delete one row from the database

I'm having a problem deleting just one row. I can insert and delete the whole table. I'm not able to understand the whole ID thing in order to delete just one row. I was looking at some examples, but I couldn't get it. It’s driving me crazy.
Here is the SQLite class;
public class datahelper {
private static final String DATABASE_NAME = "table.db";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "table1";
private Context context;
private SQLiteDatabase db;
private SQLiteStatement insertStmt;
private static final String INSERT =
"insert into " + TABLE_NAME + "(name) values (?)";
public datahelper(Context context) {
this.context = context;
OpenHelper openHelper = new OpenHelper(this.context);
this.db = openHelper.getWritableDatabase();
this.insertStmt = this.db.compileStatement(INSERT);
}
public long insert(String name) {
this.insertStmt.bindString(1, name);
return this.insertStmt.executeInsert();
}
public long insert2(String name) {
this.insertStmt2.bindString(1, name);
return this.insertStmt2.executeInsert();
}
public void deleteAll() {
this.db.delete(TABLE_NAME, null, null);
}
private static class OpenHelper extends SQLiteOpenHelper {
OpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TABLE_NAME +
" (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)");
}
}
Execute the query
DELETE FROM TABLE_NAME WHERE id = SOMEVALUE
It looks like you are using this API, which provides this delete method. My guess is that you would do this:
public void delete(int id) {
this.db.delete(TABLE_NAME, 'id = ?', new String[] { id.toString() });
}
(Original answer...)
Use a DELETE statement with a WHERE clause that deletes only the row with the id you want to remove:
DELETE FROM <tablename> WHERE id = ?
Of course, you need to know the id in order to do this. SQLite provides a function — sqlite3_last_insert_rowid() — that you can call immediately after an INSERT. If your API doesn't provide this function directly, you can get it indirectly via the equivalent SQL function:
SELECT last_insert_rowid()
Alernatively if you want to delete a certain name (assuming it's unique):
DELETE FROM <tablename> WHERE name = ?