Skip to content

Commit bb7321d

Browse files
authored
1 parent 4104477 commit bb7321d

File tree

6 files changed

+144
-5
lines changed

6 files changed

+144
-5
lines changed

spring-data-jdbc-ydb/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151

5252
<junit5.version>5.10.2</junit5.version>
5353
<lombok.version>1.18.30</lombok.version>
54-
<spring.version>3.2.1</spring.version>
54+
<spring.version>3.4.0</spring.version>
5555
<liquibase.version>4.24.0</liquibase.version>
5656

5757
<ydb.sdk.version>2.2.9</ydb.sdk.version>

spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/dialect/YdbDialect.java

+17-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package tech.ydb.data.core.dialect;
22

3+
import java.lang.reflect.Method;
34
import java.util.function.Function;
5+
46
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
57
import org.springframework.data.relational.core.dialect.AbstractDialect;
68
import org.springframework.data.relational.core.dialect.InsertRenderContext;
@@ -10,10 +12,12 @@
1012
import org.springframework.data.relational.core.sql.IdentifierProcessing;
1113
import org.springframework.data.relational.core.sql.LockOptions;
1214
import org.springframework.data.relational.core.sql.Select;
15+
1316
import tech.ydb.data.repository.ViewIndex;
1417

1518
/**
1619
* @author Madiyar Nurgazin
20+
* @author Mikhail Polivakha
1721
*/
1822
public class YdbDialect extends AbstractDialect {
1923
public static final YdbDialect INSTANCE = new YdbDialect();
@@ -55,13 +59,23 @@ public LockClause.Position getClausePosition() {
5559
protected Function<Select, CharSequence> getAfterFromTable() {
5660
return select -> {
5761
var tables = select.getFrom().getTables();
62+
5863
if (tables.size() != 1) {
5964
return "";
6065
}
6166

62-
var viewIndex = ExposeInvocationInterceptor.currentInvocation()
63-
.getMethod()
64-
.getAnnotation(ViewIndex.class);
67+
Method repositoryMethod = null;
68+
try {
69+
repositoryMethod = ExposeInvocationInterceptor.currentInvocation().getMethod();
70+
} catch (IllegalStateException e) {
71+
// the assumption is that JdbcRepositoryBeanPostProcessor made a choice to not expose metadata for this Spring Data JDBC repository
72+
}
73+
74+
if (repositoryMethod == null) {
75+
return "";
76+
}
77+
78+
var viewIndex = repositoryMethod.getAnnotation(ViewIndex.class);
6579

6680
if (viewIndex != null && (viewIndex.tableName().isEmpty() ||
6781
viewIndex.tableName().equals(tables.get(0).getName().toSql(IdentifierProcessing.NONE)))) {

spring-data-jdbc-ydb/src/main/java/tech/ydb/data/repository/config/AbstractYdbJdbcConfiguration.java

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package tech.ydb.data.repository.config;
22

33
import org.springframework.context.annotation.Configuration;
4+
import org.springframework.context.annotation.Import;
45
import org.springframework.context.annotation.Lazy;
56
import org.springframework.data.jdbc.core.convert.DefaultJdbcTypeFactory;
67
import org.springframework.data.jdbc.core.convert.JdbcArrayColumns;
@@ -15,9 +16,12 @@
1516

1617
/**
1718
* @author Madiyar Nurgazin
19+
* @author Mikhail Polivakha
1820
*/
1921
@Configuration
22+
@Import(JdbcRepositoryBeanPostProcessor.class)
2023
public class AbstractYdbJdbcConfiguration extends AbstractJdbcConfiguration {
24+
2125
@Override
2226
public JdbcConverter jdbcConverter(
2327
JdbcMappingContext mappingContext,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package tech.ydb.data.repository.config;
2+
3+
import java.util.Arrays;
4+
5+
import org.springframework.beans.BeansException;
6+
import org.springframework.beans.factory.InitializingBean;
7+
import org.springframework.beans.factory.config.BeanPostProcessor;
8+
import org.springframework.core.annotation.AnnotationUtils;
9+
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactoryBean;
10+
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
11+
import org.springframework.util.ReflectionUtils;
12+
13+
import tech.ydb.data.repository.ViewIndex;
14+
15+
/**
16+
* Enabling the exposure of the metadata for the {@link JdbcRepositoryFactoryBean}. Enables
17+
* only for those {@link RepositoryFactoryBeanSupport factory beans} that have any {@link ViewIndex}
18+
* annotated methods.
19+
*
20+
* @author Mikhail Polivakha
21+
*/
22+
@SuppressWarnings("rawtypes")
23+
public class JdbcRepositoryBeanPostProcessor implements BeanPostProcessor {
24+
25+
@Override
26+
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
27+
if (bean instanceof JdbcRepositoryFactoryBean rfbs && hasAnyViewIndexMethods(rfbs)) {
28+
rfbs.setExposeMetadata(true);
29+
}
30+
return bean;
31+
}
32+
33+
/**
34+
* Unfortunately, {@link JdbcRepositoryFactoryBean#getRepositoryInformation()} call is not possible at this stage, since
35+
* {@link RepositoryFactoryBeanSupport#factory} is not initialized at this point yet. Still, we have
36+
* to use {@link BeanPostProcessor#postProcessBeforeInitialization(Object, String)} since the
37+
* {@link RepositoryFactoryBeanSupport#setExposeMetadata(boolean) expose metadata} call needs to be done before the {@link InitializingBean#afterPropertiesSet()}
38+
* to be propagated into the underlying {@link org.springframework.data.repository.core.support.RepositoryFactorySupport factory support}
39+
*/
40+
private static boolean hasAnyViewIndexMethods(JdbcRepositoryFactoryBean rfbs) {
41+
return Arrays
42+
.stream(ReflectionUtils.getAllDeclaredMethods(rfbs.getObjectType()))
43+
.anyMatch(method -> AnnotationUtils.getAnnotation(method, ViewIndex.class) != null);
44+
}
45+
}

spring-data-jdbc-ydb/src/test/java/tech/ydb/data/YdbJdbcConfiguration.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@
88

99
/**
1010
* @author Madiyar Nurgazin
11+
* @author Mikhail Polivakha
1112
*/
1213
@Configuration
13-
@EnableJdbcRepositories
14+
@EnableJdbcRepositories(
15+
considerNestedRepositories = true,
16+
basePackages = "tech.ydb.data"
17+
)
1418
@EnableJdbcAuditing
1519
@Import(AbstractYdbJdbcConfiguration.class)
1620
public class YdbJdbcConfiguration {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package tech.ydb.data.repository.config;
2+
3+
import java.lang.reflect.Field;
4+
import java.util.Optional;
5+
6+
import org.assertj.core.api.Assertions;
7+
import org.junit.jupiter.api.Test;
8+
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.springframework.data.annotation.Id;
10+
import org.springframework.data.relational.core.mapping.Table;
11+
import org.springframework.data.repository.CrudRepository;
12+
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
13+
import org.springframework.util.ReflectionUtils;
14+
15+
import tech.ydb.data.YdbBaseTest;
16+
import tech.ydb.data.repository.ViewIndex;
17+
18+
/**
19+
* Tests for {@link JdbcRepositoryBeanPostProcessor}.
20+
*
21+
* @author Mikhail Polivakha
22+
*/
23+
class JdbcRepositoryBeanPostProcessorTest extends YdbBaseTest {
24+
25+
@Autowired
26+
private RepositoryFactoryBeanSupport<UserRepository, User, Long> userFactoryBeanSupport;
27+
28+
@Autowired
29+
private RepositoryFactoryBeanSupport<AddressRepository, Address, Long> addressFactoryBeanSupport;
30+
31+
@Test
32+
void shouldExposeMetadataOnlyForRepositoriesWithViewIndexMethods() {
33+
34+
// given.
35+
Field exposeMetadataField = ReflectionUtils.findField(RepositoryFactoryBeanSupport.class, "exposeMetadata");
36+
exposeMetadataField.setAccessible(true);
37+
38+
// when.
39+
Object userFactoryExposedMetadata = ReflectionUtils.getField(exposeMetadataField, userFactoryBeanSupport);
40+
Object addressFactoryExposedMetadata = ReflectionUtils.getField(exposeMetadataField, addressFactoryBeanSupport);
41+
42+
// then.
43+
Assertions.assertThat(userFactoryExposedMetadata).isEqualTo(true);
44+
Assertions.assertThat(addressFactoryExposedMetadata).isEqualTo(false);
45+
}
46+
47+
@Table
48+
static class User {
49+
50+
@Id
51+
private Long id;
52+
53+
private String name;
54+
}
55+
56+
@Table
57+
static class Address {
58+
59+
@Id
60+
private Long id;
61+
}
62+
63+
interface UserRepository extends CrudRepository<User, Long> {
64+
65+
@ViewIndex(indexName = "name_authors_index", tableName = "authors")
66+
Optional<User> findUserByName(String name);
67+
}
68+
69+
interface AddressRepository extends CrudRepository<Address, Long> {
70+
71+
}
72+
}

0 commit comments

Comments
 (0)