From cd7403907a56d33853fae4c46bc135c90552aca2 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Sat, 21 Apr 2018 13:53:56 +0900 Subject: [PATCH] DATAJDBC-106 - Support to specify a database object names using annotations. @Table and @Column annotation added for configuring table and column names. Original pull request: #65. --- .../data/jdbc/mapping/model/Column.java | 40 ++++++ .../jdbc/mapping/model/NamingStrategy.java | 18 ++- .../data/jdbc/mapping/model/Table.java | 42 ++++++ .../model/NamingStrategyUnitTests.java | 122 ++++++++++++++++++ 4 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/springframework/data/jdbc/mapping/model/Column.java create mode 100644 src/main/java/org/springframework/data/jdbc/mapping/model/Table.java create mode 100644 src/test/java/org/springframework/data/jdbc/mapping/model/NamingStrategyUnitTests.java diff --git a/src/main/java/org/springframework/data/jdbc/mapping/model/Column.java b/src/main/java/org/springframework/data/jdbc/mapping/model/Column.java new file mode 100644 index 000000000..ae7973758 --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/mapping/model/Column.java @@ -0,0 +1,40 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jdbc.mapping.model; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The annotation to configure a mapping database column. + * + * @author Kazuki Shimizu + * @since 1.0 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD}) +@Documented +public @interface Column { + + /** + * The mapping column name. + */ + String value(); + +} diff --git a/src/main/java/org/springframework/data/jdbc/mapping/model/NamingStrategy.java b/src/main/java/org/springframework/data/jdbc/mapping/model/NamingStrategy.java index e796336bf..976b15513 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/model/NamingStrategy.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/model/NamingStrategy.java @@ -15,6 +15,10 @@ */ package org.springframework.data.jdbc.mapping.model; +import org.springframework.core.annotation.AnnotatedElementUtils; + +import java.util.Optional; + /** * Interface and default implementation of a naming strategy. Defaults to no schema, table name based on {@link Class} * and column name based on {@link JdbcPersistentProperty}. @@ -45,17 +49,23 @@ public interface NamingStrategy { } /** - * Look up the {@link Class}'s simple name. + * Look up the {@link Class}'s simple name or {@link Table#value()}. */ default String getTableName(Class type) { - return type.getSimpleName(); + Table table = AnnotatedElementUtils.findMergedAnnotation(type, Table.class); + return Optional.ofNullable(table)// + .map(Table::value)// + .orElse(type.getSimpleName()); } /** - * Look up the {@link JdbcPersistentProperty}'s name. + * Look up the {@link JdbcPersistentProperty}'s name or {@link Column#value()}. */ default String getColumnName(JdbcPersistentProperty property) { - return property.getName(); + Column column = property.findAnnotation(Column.class); + return Optional.ofNullable(column)// + .map(Column::value)// + .orElse(property.getName()); } default String getQualifiedTableName(Class type) { diff --git a/src/main/java/org/springframework/data/jdbc/mapping/model/Table.java b/src/main/java/org/springframework/data/jdbc/mapping/model/Table.java new file mode 100644 index 000000000..1fc0234b0 --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/mapping/model/Table.java @@ -0,0 +1,42 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jdbc.mapping.model; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The annotation to configure a mapping database table. + * + * @author Kazuki Shimizu + * @since 1.0 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +@Inherited +public @interface Table { + + /** + * The mapping table name. + */ + String value(); + +} diff --git a/src/test/java/org/springframework/data/jdbc/mapping/model/NamingStrategyUnitTests.java b/src/test/java/org/springframework/data/jdbc/mapping/model/NamingStrategyUnitTests.java new file mode 100644 index 000000000..4aa05afc4 --- /dev/null +++ b/src/test/java/org/springframework/data/jdbc/mapping/model/NamingStrategyUnitTests.java @@ -0,0 +1,122 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jdbc.mapping.model; + +import org.junit.Test; +import org.springframework.data.annotation.Id; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Unit tests for the {@link NamingStrategy}. + * + * @author Kazuki Shimizu + */ +public class NamingStrategyUnitTests { + + private final NamingStrategy target = NamingStrategy.INSTANCE; + private final JdbcMappingContext context = new JdbcMappingContext(target, mock(NamedParameterJdbcOperations.class), mock(ConversionCustomizer.class)); + private final JdbcPersistentEntity persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class); + + @Test + public void getTableName() { + assertThat(target.getTableName(persistentEntity.getType())) + .isEqualTo("DummyEntity"); + assertThat(target.getTableName(DummySubEntity.class)) + .isEqualTo("dummy_sub_entity"); // DATAJDBC-106 + } + + @Test // DATAJDBC-106 + public void getTableNameWithTableAnnotation() { + assertThat(target.getTableName(DummySubEntity.class)) + .isEqualTo("dummy_sub_entity"); + } + + @Test + public void getColumnName() { + assertThat(target.getColumnName(persistentEntity.getPersistentProperty("id"))) + .isEqualTo("id"); + assertThat(target.getColumnName(persistentEntity.getPersistentProperty("createdAt"))) + .isEqualTo("createdAt"); + assertThat(target.getColumnName(persistentEntity.getPersistentProperty("dummySubEntities"))) + .isEqualTo("dummySubEntities"); + } + + @Test // DATAJDBC-106 + public void getColumnNameWithColumnAnnotation() { + assertThat(target.getColumnName(persistentEntity.getPersistentProperty("name"))) + .isEqualTo("dummy_name"); + assertThat(target.getColumnName(persistentEntity.getPersistentProperty("lastUpdatedAt"))) + .isEqualTo("dummy_last_updated_at"); + } + + @Test + public void getReverseColumnName() { + assertThat(target.getReverseColumnName(persistentEntity.getPersistentProperty("dummySubEntities"))) + .isEqualTo("DummyEntity"); + } + + @Test + public void getKeyColumn() { + assertThat(target.getKeyColumn(persistentEntity.getPersistentProperty("dummySubEntities"))) + .isEqualTo("DummyEntity_key"); + } + + @Test + public void getSchema() { + assertThat(target.getSchema()) + .isEmpty(); + } + + @Test + public void getQualifiedTableName() { + assertThat(target.getQualifiedTableName(persistentEntity.getType())) + .isEqualTo("DummyEntity"); + + NamingStrategy strategy = new NamingStrategy() { + @Override + public String getSchema() { + return "schema"; + } + }; + assertThat(strategy.getQualifiedTableName(persistentEntity.getType())) + .isEqualTo("schema.DummyEntity"); + } + + private static class DummyEntity { + @Id + private int id; + @Column("dummy_name") + private String name; + private LocalDateTime createdAt; + private LocalDateTime lastUpdatedAt; + private List dummySubEntities; + @Column("dummy_last_updated_at") + public LocalDateTime getLastUpdatedAt() { + return LocalDateTime.now(); + } + } + + @Table("dummy_sub_entity") + private static class DummySubEntity { + } + +}