JPA Hibernate + UCP Oracle. Query executed from save method does not use the index of the table - spring

I'm using the interface CRUDRepository in order to use the save method in other class where Repository is injected.
This method does an insert and a select for retrieve this object inserted from database, I mean.
The query executed is quite simple:
select auditoriab0_.adb_seqitm as adb_seqitm1_1_0_,
auditoriab0_.adb_codprv as adb_codprv2_1_0_, auditoriab0_.adb_ideses as adb_ideses3_1_0_,
auditoriab0_.adb_locata as adb_locata4_1_0_, auditoriab0_.adb_rqores as adb_rqores5_1_0_,
auditoriab0_.adb_rstime as adb_rstime6_1_0_, auditoriab0_.adb_subprv as adb_subprv7_1_0_,
auditoriab0_.adb_swierr as adb_swierr8_1_0_, auditoriab0_.adb_tiptrz as adb_tiptrz9_1_0_,
auditoriab0_.adb_ubitrz as adb_ubitrz10_1_0_, auditoriab0_.adb_xmltxt as adb_xmltxt11_1_0_
from nwt00.auditoria_bus_gsi auditoriab0_ where auditoriab0_.adb_seqitm=:p1
Where adb_seqitm column has and index on it (It's the table's primary key).
If this query is executed on SQLDeveloper, for instance, the explain plan is the correct one (access by rowid).
However, if this query is executed by hibernate, the result is a full scan.
Could you help me with this issue? I will be grateful because I don't have seen a real solution on internet for this specific problem.
Thank you in advance.
This behaviour happens with the ucp (universal connection pool) pool. My database bean configuration is the next (variables are setted by application.properties file):
UniversalConnectionPoolManager mgr;
try {
mgr = UniversalConnectionPoolManagerImpl. getUniversalConnectionPoolManager();
mgr.destroyConnectionPool("hotels");
} catch (UniversalConnectionPoolException e) {
}
PoolDataSourceImpl poolDataSource = (PoolDataSourceImpl) PoolDataSourceFactory.getPoolDataSource();
poolDataSource.setUser(userName);
poolDataSource.setPassword(passWord);
poolDataSource.setURL(url);
poolDataSource.setConnectionFactoryClassName(driver);
poolDataSource.setConnectionPoolName("hotels");
poolDataSource.setInitialPoolSize(initialNumConnections);
poolDataSource.setMinPoolSize(minNumConnections);
poolDataSource.setMaxPoolSize(maxNumConnections);
poolDataSource.setMaxConnectionReuseTime(reconnectTime);
poolDataSource.setMaxConnectionReuseCount(maxReconnectCount);
poolDataSource.setTimeToLiveConnectionTimeout(timeToLive);
poolDataSource.setConnectionWaitTimeout(connectWaitTimeOut);
poolDataSource.setValidateConnectionOnBorrow(true);
poolDataSource.setInactiveConnectionTimeout(inactiveConnectionTimeOut);
Properties properties = new Properties();
properties.put("v$session.program", "xxxx");
properties.put("defaultNChar", "false");
poolDataSource.setConnectionProperties(properties );
I'm using Spring Boot + Spring Data JPA. These are my dependencies of my pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.4.0</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ucp</artifactId>
<version>11.2.0.4.0</version>
</dependency>

#krokodilko As you anticipated, the error was between database type and java type. In DDBB, this field is a String (varchar2(15)). In Java, this field is mapped with a long type. I guess hibernate or database is doing a conversion which breaks the index. I have changed the java type by String type and query is working successfully. Explain plan is correct.

Related

Spring Boot Application - H2 Database (1.4.200) Array column, Hibernate mapping

I'm creating a Spring Boot application, where I have a Routes table. Within the Routes table I have stops column where I want to store the integer array of stop id.
Since H2 database (version 1.4.200) doesn't support typed array, I defined it as generic array.
CREATE TABLE Routes (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(250) NOT NULL,
stops ARRAY[100] NOT NULL
);
INSERT INTO Routes (id, name, stops) VALUES (1, '500CK', (1,2));
SELECT * FROM Routes;
ID NAME STOPS
1 500CK [1, 2]
I want to know what should be the type of stops property (any other configuration required) so it works with Hibernate
#Entity
#Table(name = "Routes")
public class Route {
#Id
#GeneratedValue
private int id;
private String name;
// I'm not sure what should be the data type of stops
private Object[] stops;
}
I'm getting below error while trying to get the route from /route endpoint
org.h2.jdbc.JdbcSQLDataException: Hexadecimal string contains non-hex character: "[1, 2]" [90004-200]
I created the Routes table by defining the DDL in the schema.sql file
DROP TABLE IF EXISTS Routes;
CREATE TABLE Routes (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(250) NOT NULL,
stops ARRAY[100] NOT NULL
);
And I inserted one record by defining the DML in data.sql file
INSERT INTO ROUTES (id, name, stops) VALUES
(1, '500CK', (1,2));
UPDATE :
I updated h2 to 2.1.212
CREATE TABLE Routes (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(250) NOT NULL,
stops INTEGER ARRAY[100] NOT NULL
);
INSERT INTO ROUTES (id, name, stops) VALUES(1, '500CK', ARRAY[1, 2]);
I can see the above record in the Routes table.
Changed the Route entity as below
#Entity
#Table(name = "Routes")
public class Route {
#Id
#GeneratedValue
private int id;
private String name;
private Integer[] stops;
// getter & setters
}
I'm getting below error
org.h2.jdbc.JdbcSQLDataException: Data conversion error converting "ARRAY to BINARY VARYING" [22018-212]
I'm using hibernate-core 5.6.8.Final
Looks like Hibernate is not able to do the conversion.
Can I achieve it with just Hibernate, Or do I have to do it, the way mentioned here
https://vladmihalcea.com/how-to-map-java-and-sql-arrays-with-jpa-and-hibernate/
Update : Trying to update hibernate-core to 6.1.0.Final
I tried to update the hibernate-core to 6.1.0.Final.
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.1.0.Final</version>
</dependency>
But I'm getting below error
Field routeRepository in net.mahtabalam.service.RouteService required a bean named 'entityManagerFactory' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean named 'entityManagerFactory' in your configuration.
Looks like this is caused due to dependency version conflicts.
Below is the project pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>net.mahtabalam</groupId>
<artifactId>routes</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>routes</name>
<description>Bus Routes</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.1.212</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Unfortunately to my knowledge there will be no way to use hibernate 6.1.0.Final with Spring Boot currently. The issue you've noted with entityManagerFactory will be because the hibernate-entitymanager project was moved into hibernate-core in version 6.1.0.Final.
Looking through the plans for Spring Boot, see here, it looks like there is no imminment plan to update the version of hibernate used either as it won't be until M3 of version 3.
Instead your best bet will be to make a custom converter, I am currently trying to tackle the exact same problem and if I find a solution I will share it.

Function "ST_WITHIN" not found in Spring Boot Application using h2 In-Memory Database and Hibernate Spatial

In a Spring Boot application (h2 in-memory database + Hibernate Spatial) in a Junit test, I'm getting error "Function "ST_WITHIN" not found" for a "within" query
pom.xml:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-spatial</artifactId>
</dependency>
test/resources/application.properties:
spring.datasource.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=va
spring.jpa.database=h2
spring.jpa.database-platform=org.hibernate.spatial.dialect.h2geodb.GeoDBDialect
LocationRepository.java
#Query(value = "select l from Location l where within(l.position, :box) = true")
List<Location> findByPositionWithin(#Param("box") org.locationtech.jts.geom.Geometry box);
LocationEntity.java
#Column(columnDefinition = "GEOMETRY")
private org.locationtech.jts.geom.Point position;
Obviously, the alias ST_WITHIN is missing.
Any ideas how to fix this?

Resource leak detected when using spring-data-redis on cloud foundry

We develop a spring-boot service, which offers a rest api (spring-webflux) and sends data via RabbitMQ (spring-rabbit). The service is deployed on cloud foundry and we use spring-boot in version 2.1.4. We added spring-boot-starter-data-redis to use redis to cache some data and we got the following error:
[io.netty.util.ResourceLeakDetector] [] LEAK: HashedWheelTimer.release() was not called before it's garbage-collected. See http://netty.io/wiki/reference-counted-objects.html for more information.
Recent access records:
Created at:
io.netty.util.HashedWheelTimer.<init>(HashedWheelTimer.java:284)
io.netty.util.HashedWheelTimer.<init>(HashedWheelTimer.java:217)
io.netty.util.HashedWheelTimer.<init>(HashedWheelTimer.java:196)
io.netty.util.HashedWheelTimer.<init>(HashedWheelTimer.java:178)
io.netty.util.HashedWheelTimer.<init>(HashedWheelTimer.java:162)
io.lettuce.core.resource.DefaultClientResources.<init>(DefaultClientResources.java:169)
io.lettuce.core.resource.DefaultClientResources$Builder.build(DefaultClientResources.java:532)
io.lettuce.core.resource.DefaultClientResources.create(DefaultClientResources.java:233)
io.lettuce.core.AbstractRedisClient.<init>(AbstractRedisClient.java:98)
io.lettuce.core.RedisClient.<init>(RedisClient.java:87)
io.lettuce.core.RedisClient.create(RedisClient.java:124)
org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.lambda$createClient$7(LettuceConnectionFactory.java:971)
java.base/java.util.Optional.orElseGet(Unknown Source)
org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.createClient(LettuceConnectionFactory.java:971)
org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.afterPropertiesSet(LettuceConnectionFactory.java:273)
org.springframework.cloud.service.keyval.RedisConnectionFactoryCreator.create(RedisConnectionFactoryCreator.java:88)
org.springframework.cloud.service.keyval.RedisConnectionFactoryCreator.create(RedisConnectionFactoryCreator.java:31)
org.springframework.cloud.Cloud.getServiceConnector(Cloud.java:288)
org.springframework.cloud.Cloud.getSingletonServiceConnector(Cloud.java:202)
org.springframework.cloud.config.java.CloudServiceConnectionFactory.redisConnectionFactory(CloudServiceConnectionFactory.java:260)
org.springframework.cloud.config.java.CloudServiceConnectionFactory.redisConnectionFactory(CloudServiceConnectionFactory.java:242)
...
This error only happens when we run the service on cloud foundry, if we run it locally, we don't get any error.
We don't do any configuration of the connection factory or the stringRedisTemplate on our side and only use stringRedisTemplate, which is configured by the spring-autoconfiguration.
We use following configuration for redis on cloud foundry:
#Configuration
#Profile( "cloud" )
public class CloudSpecificConfig extends AbstractCloudConfig {
#Bean
public RedisConnectionFactory redisConnectionFactory() {
return connectionFactory().redisConnectionFactory();
}
}
And this is how we use the template
#Component
#RequiredArgsConstructor
public final class RequestUtil {
private final StringRedisTemplate myRedisTemplate;
public String cacheId(String id, String value) {
myRedisTemplate.opsForValue().set( id, value );
}
}
These are our spring dependencies:
<properties>
<spring-boot-version>2.1.4.RELEASE</spring-boot-version>
</properties>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>${spring-boot-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring-boot-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>${spring-boot-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
<version>${spring-boot-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cloud-connectors</artifactId>
<version>${spring-boot-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>${spring-boot-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${spring-boot-version}</version>
</dependency>
We are quite confused on our side, since we didn't do any specific configuration on our side. It looks for us like there is something wrong with the spring configuration on the cloud. Are we doing something wrong? Do we need to configure something differently? Is this a bug?
This is what I had to do
I will see if I can find a more elegant way
#Bean(destroyMethod = "shutdown")
public DefaultClientResources lettuceClientResources() {
return DefaultClientResources.create();
}
#SuppressWarnings("unused")
#Bean
public RedisConnectionFactory redisConnectionFactory(DefaultClientResources dependency) {
return connectionFactory().redisConnectionFactory("redis-pcf-service");
}
In the end the issue disappeared on our side, because we changed the redis client from lettuce to jedis.
We had the problem with lettuce that we would lose the connection to our redis service on our cloud infrastructure. But since there was an update to the redis service at same time as we changed the client, we don't really know if it was related to lettuce.
Maybe there also just something wrong in the auto-configuration in conjunction with the redis service on our cloud instructure, which is based on cloudfoundry

Parse JSON using Spring SPEL

Can someone tell me why this does not work:
#Test
public void should_parse_json() {
Expression expression = new SpelExpressionParser().parseExpression("#jsonPath(get('JsonData'), '$.someData')");
Map<String, Object> data = new HashMap<>();
data.put("JsonData", "{\"someData\": 100}");
StandardEvaluationContext context = new StandardEvaluationContext(data);
context.addPropertyAccessor(new JsonPropertyAccessor());
assertThat(expression.getValue(context, Object.class)).isEqualTo(100);
}
I get error "org.springframework.expression.spel.SpelEvaluationException: EL1006E: Function 'jsonPath' could not be found"
And I have following jar in classpath:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
</dependency>
The SPEL documentation did not help me.
Such a #jsonPath() SpEL function is a part of Spring Integration infrastructure: https://docs.spring.io/spring-integration/docs/current/reference/html/spel.html#spel-functions.
It's not going to work with the plain Spring and only SpEL.
However I see that you use a JsonPropertyAccessor. This one indeed a part of Spring Integration and you definitely have that in your classpath.
From here I think you just miss to register a SpEL function into the StandardEvaluationContext: https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#expressions-ref-functions
context.registerFunction("jsonPath", BeanUtils.resolveSignature("evaluate", JsonPathUtils.class));
Add dependencies.
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.7.0</version>
</dependency>
Register SpEL function
evaluationContext.registerFunction("jsonPath", BeanUtils.resolveSignature("evaluate", JsonPathUtils.class));

Spring data JPA and Geometry type

I am developing an application that will run on both MySql and MS SQL.
I have a field that is "geometry" type for spatial.
By using:
#Column(columnDefinition = "geometry")
private Point geometry;
(point is org.springframework.data.geo.Point)
Hibernate creates the field properly (hbm2ddl).
But inserting any point does not work.
I get : Data truncation: Cannot get geometry object from data you send to the GEOMETRY field
I use spring-boot-jpa-starter.. and not direct hibernate.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-spatial</artifactId>
<version>5.2.2.Final</version>
</dependency>
Regards,
Ido
Hello I have successfully mapped a point in JPA. Here's what I did:
I have this on Maven:
<dependency>
<groupId>com.vividsolutions</groupId>
<artifactId>jts</artifactId>
<version>1.13</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-spatial</artifactId>
<version>5.2.5.Final</version>
</dependency>
I have this on my entity:
#Column(name = "locationpoint", columnDefinition = "POINT")
private Point locationpoint;
I have this on my application.properties:
# needed for Location domain class
spring.jpa.properties.hibernate.dialect=org.hibernate.spatial.dialect.mysql.MySQL56InnoDBSpatialDialect
I can get the value using this:
locationRepository.findOne((long) 1).getLocationpoint().getX();
locationRepository.findOne((long) 1).getLocationpoint().getY();
I based my solution from here Matti Tahvonen's example:
https://github.com/mstahv/spring-boot-spatial-example
Hope this helps.

Resources