spring boot application showing ??? characters instead of unicode - spring-boot

While reading from database the logs in my dao class shows junk characters in place of unicode characters. Sql developer shows correct values from oracle database also correct NLS language encoding is set on the database.
Below code works for standard jdbc:
connection = DriverManager.getConnection(
"jdbc:oracle:thin:#localhost:1521:xe", "adminuser", "oracle");
Statement st=connection.createStatement();
ResultSet res=st.executeQuery("SELECT menu_item_name from pending_menu_item
where menu_item_id=6062");
while(res.next()){
System.out.println("itemName: "+res.getString(1));
}
Below is the url for springboot project which shows junk characters, I
uploaded to git hub.https://github.com/AyubOpen/spring-boot-jdbc/
package com.mkyong;
import com.mkyong.dao.CustomerRepository;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
import java.util.List;
import static java.lang.System.exit;
#SpringBootApplication
public class SpringBootConsoleApplication implements CommandLineRunner {
#Autowired
DataSource dataSource;
#Autowired
private CustomerRepository customerRepository;
#Autowired
private JdbcTemplate jdbcTemplate;
public static void main(String[] args) throws Exception {
SpringApplication.run(SpringBootConsoleApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
// If you want to check the HikariDataSource settings
HikariDataSource newds = (HikariDataSource)dataSource;
System.out.println("getMaximumPoolSize = " + ((HikariDataSource)
dataSource).getMaximumPoolSize());
System.out.println("DATASOURCE = " +
newds.getDataSourceProperties().getProperty("hikari.useUnicode"));
if (args.length <= 0) {
System.err.println("[Usage] java xxx.jar {display}");
} else {
if (args[0].equalsIgnoreCase("display")) {
System.out.println("Display items...");
List<String> list = customerRepository.findAll();
list.forEach(x -> System.out.println(x));
}
System.out.println("Done!");
}
exit(0);
}
}
package com.mkyong.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository
public class CustomerRepository {
#Autowired
private JdbcTemplate jdbcTemplate;
public List<String> findAll() {
List<String> result = jdbcTemplate.query(
"SELECT menu_item_name from pending_menu_item where
menu_item_id=6062",
(rs, rowNum) -> rs.getString("menu_item_name")
);
return result;
}
}
application.properties
----------------------
spring.main.banner-mode=off
spring.datasource.initialize=true
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.url=jdbc:oracle:thin:#localhost:1521/xe
spring.datasource.username=jahezdbapp
spring.datasource.password=oracle
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.hikari.useUnicode=true
spring.datasource.hikari.characterEncoding=utf-8
spring.datasource.hikari.characterSetResults=utf8
# HikariCP settings
#60 sec
spring.datasource.hikari.connection-timeout=60000
# max 5
spring.datasource.hikari.maximum-pool-size=5

Add ?useUnicode=yes&characterEncoding=UTF-8 at the end of spring.datasource.url.
Set spring.datasource.sqlScriptEncoding=UTF-8 in application.properties
1 itself should solve the issue, 2 may not be necessary.

Especially for Oracle. At least I was in the same situation.
It seems, your database uses some national (non-unicode) encoding. So JDBC cannot translate it to UNICODE.
Oracle JDBC by default supports only few character sets: US7ASCII, WE8DEC, WE8ISO8859P1, WE8MSWIN1252, and UTF8. So all strings encoded in different encodings will be displayed as questions. To add support of all other character sets add to the application classpath the file orai18n.jar.
See more here: https://docs.oracle.com/database/121/JJDBC/global.htm#JJDBC28643

This issue does not seem to be with jdbc datasource, I will create a simple github project for springboot and followup on this issue with the below question:
spring boot commandline runner is using windows default character encoding

Record:
worked url for DB2, jdb
jdbc:db2://x.x.x.x:5010/xxxx:useUnicode=yes;characterEncoding=UTF-8;

Had the same issue when reading unicodes. Add this line to the .bat file
-Dfile.encoding=UTF8

Related

Why isn't the configuration from my application-test.properties being used?

I am trying to run tests in my Spring application which need to use an embedded h2 database (instead of the sql database I use for the actual application). The problem I was facing was that it is executing all queries on the h2 database in upper case and it was failing because of that.
org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "MOOD" not found; SQL statement:
insert into mood
Now I figured out in application properties you can just put DATABASE_TO_LOWER=TRUE in the connection string, but this is not working for me. I can clearly see in the output that is not using the url I am defining in application-test.properties:
Starting embedded database: url='jdbc:h2:mem:85afe142-af28-4c3a-8226-0a77abdb004d;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'
So the relevant files:
my application-test.properties:
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY-2;DATABASE_TO_LOWER=TRUE
spring.datasource.username=sa
spring.datasource.password=sa
my test class:
package com.example.demo.persistence.interfaces;
import com.example.demo.persistence.dto.Mood;
import com.example.demo.persistence.dto.MoodType;
import com.example.demo.persistence.dto.Patient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import java.util.Calendar;
import java.util.Collection;
import static org.junit.jupiter.api.Assertions.*;
#DataJpaTest
#ActiveProfiles("test")
#TestPropertySource(locations="classpath:application-test.properties")
#EnableConfigurationProperties
class MoodRepositoryTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
private MoodRepository repository;
#Autowired
private PatientRepository patientRepository;
#Test
void getMoodsByPatient() {
final Patient patient = new Patient("Kees", "spek", "straat", Calendar.getInstance().getTime());
final Patient patient2 = new Patient("Kees2", "spek2", "straat2", Calendar.getInstance().getTime());
final Mood mood1 = new Mood(patient, MoodType.GOOD,"","","","","","","",Calendar.getInstance().getTime());
final Mood mood2 = new Mood(patient, MoodType.GOOD,"","","","","","","",Calendar.getInstance().getTime());
final Mood mood3 = new Mood(patient2, MoodType.GOOD,"","","","","","","",Calendar.getInstance().getTime());
entityManager.persist(patient);
entityManager.persist(patient2);
entityManager.persist(mood1);
entityManager.persist(mood2);
entityManager.persist(mood3);
final int expectedMoodsFound = 2;
final int actualMoodsFound = ((Collection<?>)repository.getMoodsByPatient(patient)).size();
assertEquals(expectedMoodsFound,actualMoodsFound);
}
#Test
void getMoodByMoodId() {
}
#Test
void deleteAllByDateBefore() {
}
}
So my problem is, I can't seem to properly load in the application-test.properties I guess? What am I doing wrong? I read so many different things on stackoverflow and other websites and none of them worked and I feel like it's a fairly simple issue.
The #DataJpaTest annotation uses the meta annotation #AutoConfigureTestDatabase which has the default configuration to replace any manually configured or auto-configured datasource:
public #interface AutoConfigureTestDatabase {
/**
* Determines what type of existing DataSource bean can be replaced.
* #return the type of existing DataSource to replace
*/
#PropertyMapping(skip = SkipPropertyMapping.ON_DEFAULT_VALUE)
Replace replace() default Replace.ANY;
/**
* What the test database can replace.
*/
enum Replace {
/**
* Replace the DataSource bean whether it was auto-configured or manually defined.
*/
ANY,
/**
* Only replace the DataSource if it was auto-configured.
*/
AUTO_CONFIGURED,
/**
* Don't replace the application default DataSource.
*/
NONE
}
}
If you want to define the database configuration on your own, you can opt-out using:
// ... existing annotations
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class MoodRepositoryTest {
// ...
}

Spring testcontainers Driver org.testcontainers.jdbc.ContainerDatabaseDriver claims to not accept jdbcUrl

Having following configuration for my integration tests I ran into following exception:
Driver org.testcontainers.jdbc.ContainerDatabaseDriver claims to not accept jdbcUrl, jdbc:postgresql://localhost:32864/test?loggerLevel=OFF
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = WebApplication.class)
#AutoConfigureMockMvc
#Testcontainers
#TestPropertySource(ResourceUtils.CLASSPATH_URL_PREFIX + "application-test.properties")
public abstract class AbstractIntegrationTest {
#Autowired
protected MockMvc mockMvc;
#Container
protected static PostgreSQLContainer<?> postgresqlContainer = new PostgreSQLContainer<>();
#DynamicPropertySource
static void postgresqlProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgresqlContainer::getJdbcUrl);
registry.add("spring.datasource.username", postgresqlContainer::getUsername);
registry.add("spring.datasource.password", postgresqlContainer::getPassword);
}
#Test
void contextLoads() {
Assertions.assertThat(mockMvc).isNotNull();
Assertions.assertThat(postgresqlContainer.isRunning()).isTrue();
}
}
The postgresqlContainer.getJdbcUrl() returns jdbc:postgresql://localhost:32864/test?loggerLevel=OFF
But it should return jdbc:tc:postgresql://..., its missing the tc part.
Any solution to this ?
Hardcoding it like: String.format("jdbc:tc:postgresql://localhost:%s/%s", postgresqlContainer.getFirstMappedPort(), postgresqlContainer.getDatabaseName()) seems to work.
What am I doing wrong here?
Please see the big orange warning here:
https://www.testcontainers.org/modules/databases/jdbc/
You should use either the JDBC URL with tc: prefix and ContainerDatabaseDriver or container instance with getJdbcUrl() and the original driver (or let the system detect the driver for you), not both.
In my case just added the postgresql dependency (it includes the driver) and it worked:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.3.0</version>
</dependency>
My test class:
import org.junit.Rule;
import org.junit.Test;
import org.junit.platform.commons.annotation.Testable;
import org.testcontainers.containers.PostgreSQLContainer;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.LogManager;
import static org.junit.jupiter.api.Assertions.assertEquals;
#Testable
public class PostgreSqlContainerLiveTest {
#Rule
public PostgreSQLContainer postgresContainer = new PostgreSQLContainer("postgres:9.4");
static {
// Postgres JDBC driver uses JUL; disable it to avoid annoying, irrelevant, stderr logs during connection testing
LogManager.getLogManager().getLogger("").setLevel(Level.OFF);
}
#Test
public void whenSelectQueryExecuted_thenResultsReturned() throws Exception {
ResultSet resultSet = performQuery(postgresContainer, "SELECT 1");
resultSet.next();
int result = resultSet.getInt(1);
assertEquals(1, result);
}
private ResultSet performQuery(PostgreSQLContainer postgreSQLContainer, String query) throws SQLException {
String jdbcUrl = postgreSQLContainer.getJdbcUrl();
String username = postgreSQLContainer.getUsername();
String password = postgreSQLContainer.getPassword();
Connection conn = DriverManager.getConnection(jdbcUrl, username, password);
return conn.createStatement().executeQuery(query);
}
}
I hope that this could help to you or someone else.
Make sure you have both dependency testcontainers postgresql & dependency postgresql at your config file.

Spring boot JPA repository committing code even if #Transactional placed in Service layer

See below spring boot code
I have used JPA repository.
Controller.
Service.
Repository
BaseController
package com.controller;
import com.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class BaseController {
#Autowired
private StudentService studentService;
#GetMapping(value = "/addStudent", produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<String> base() {
studentService.save();
return new ResponseEntity<String>("SUCCESS", HttpStatus.OK);
}
}
StudentService.java
package com.service;
import com.model.Student;
import com.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
#Service("studentService")
public class StudentServiceImpl implements StudentService {
#Autowired
private StudentRepository studentRepository;
#Override
#Transactional
public Student save() {
Student student = new Student();
student.setFirstName("ABC");
student.setLastName("PQR");
studentRepository.save(student);
int i = 10 / 0; //Error code
return student;
}
}
StudentRepository
package com.repository;
import com.model.Student;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository("studentRepository")
public interface StudentRepository extends CrudRepository<Student, Long> {
public List<Student> findAll();
}
Application.properties
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
#maximum number of milliseconds that a client will wait for a connection
spring.datasource.hikari.connection-timeout = 20000
#minimum number of idle connections maintained by HikariCP in a connection pool
spring.datasource.hikari.minimum-idle= 10
#maximum pool size
spring.datasource.hikari.maximum-pool-size= 10
#maximum idle time for connection
spring.datasource.hikari.idle-timeout=10000
# maximum lifetime in milliseconds of a connection in the pool after it is closed.
spring.datasource.hikari.max-lifetime= 1000
#default auto-commit behavior.
spring.datasource.hikari.auto-commit =false
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.jdbcUrl=jdbc:mysql://localhost:3306/demo?autoReconnect=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.show-sql=true
spring.jpa.properties..hibernate.format_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.hibernate.ddl-auto=update
After executing save method from StudentRepository, data get inserted
immediately into database. no rollback or any other isolation levels are
working in StudentServiceImpl.java even if Error code is there.
I have tried to set "spring.datasource.hikari.auto-commit =true" setting value true, Placed #Transaction at top of the StudentServiceImpl.java class but still it didn't worked.
You do not need to mess with any hikari settings, and certainly not with autoCommit(true) as this DISABLES transactions. Delete all these properties.
Where is the "error" in your code? Spring rolls back on unchecked exceptions being thrown (not checked ones or errors), I cannot see that in your code.
What behavior do you expect? It looks fine to me.
It's probably beacause of Open Jpa in View behaviour.
Write the following line in your properties file:
spring.jpa.open-in-view=false
take a look at this if you want to know more.

Is it possible to set the default date format in JSON-B (Yasson) globally, instead of adding an annotation on every property?

I have using Jersey so far and I am doing my first implementation with JSON-B.
I am using Payara, so I working with Jersey and Yasson. I had an issue, because the serialized dates would always contain the "[UTC]" suffix.
I have managed to use an annotation on my date property, in my DTO. But I would like to configure that globally (in the JAX-RS application config?), instead of repeating myself on every date property. Is that possible? I haven't found anything so far...
Side question: I assume that it is possible to get rid of this "[UTC]" suffix, since it breaks all clients trying to parse the date. Any idea?
Thanks to this Github issue, I was able to solve my problem. Here is what I ended up writing in my code:
JSONConfigurator.java:
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
import javax.json.bind.config.PropertyNamingStrategy;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
#Provider
public class JSONConfigurator implements ContextResolver<Jsonb> {
#Override
public Jsonb getContext(Class<?> type) {
JsonbConfig config = getJsonbConfig();
return JsonbBuilder
.newBuilder()
.withConfig(config)
.build();
}
private JsonbConfig getJsonbConfig() {
return new JsonbConfig()
.withDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", null);
}
}
And:
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;
#ApplicationPath("/api")
public class ApplicationConfig extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<Class<?>>();
addRestResourceClasses(resources);
resources.add(JSONConfigurator.class);
return resources;
}
private void addRestResourceClasses(Set<Class<?>> resources) {
...
}
}

Error Messages as Key Value Pairs - from a Properties File in classpath - Spring boot 2.0

We are currently on a Spring Boot Version 1.x
We have Error Messages (Error Key -> Error Code) pairs in our error.properties file (this is in the class path).
We leveraged PropertiesConfigurationFactory to get these Error Key and Error Code pairs in to a POJO, this POJO had a Map
Hence very convenient to be used across our application to get an Error code for a given Error Key.
What is its equivalent in Spring Boot 2.x ?.
Assuming you have error.properties file with the below contents:
errors.error1=101
errors.error2=102
errors.error3=103
A simple spring boot app that demonstrates the injection of these properties :
package snmaddula.remittance;
import java.util.Map;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
#SpringBootApplication
#ConfigurationProperties
#PropertySource("classpath:error.properties")
public class DemoApplication {
private Map<String, Integer> errors;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public CommandLineRunner cli() {
return (args) -> {
System.out.println(errors); // you can print and see the error properties injected to this map.
};
}
public void setErrors(Map<String, Integer> errors) {
this.errors = errors;
}
}
With the use of #PropertySource and #ConfigurationProperties we can enable property injection provided we have a setter method for our attribute.
When you run this program, you can see the properties getting printed on to the console as I added a CommandLineRunner cli() {..} to show the working of it.
The working sample is available on GitHub.

Resources