Skip to content

Commit 720a0a3

Browse files
committed
DATAJDBC-95 - Saving and loading a trivial entity.
Creation of the necessary sql statements is now dynamic and driven in a trivial way by the entity. Known issues with the solution that need to get fixed later: Sql generating code is in the repository and should go somewhere else. Mapping logic is very trivial and should go in a separate place.
1 parent 11df79b commit 720a0a3

File tree

4 files changed

+162
-23
lines changed

4 files changed

+162
-23
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jdbc.repository;
17+
18+
import java.lang.reflect.InvocationTargetException;
19+
import java.sql.ResultSet;
20+
import java.sql.SQLException;
21+
import org.springframework.data.mapping.PersistentEntity;
22+
import org.springframework.data.mapping.PersistentProperty;
23+
import org.springframework.data.mapping.PropertyHandler;
24+
25+
/**
26+
* @author Jens Schauder
27+
*/
28+
class EntityRowMapper<T> implements org.springframework.jdbc.core.RowMapper<T> {
29+
30+
private final PersistentEntity<T, ?> entity;
31+
32+
EntityRowMapper(PersistentEntity<T, ?> entity) {
33+
this.entity = entity;
34+
}
35+
36+
@Override
37+
public T mapRow(ResultSet rs, int rowNum) throws SQLException {
38+
39+
try {
40+
41+
T t = createInstance();
42+
43+
entity.doWithProperties((PropertyHandler) property -> {
44+
setProperty(rs, t, property);
45+
});
46+
47+
return t;
48+
} catch (Exception e) {
49+
throw new RuntimeException(String.format("Could not instantiate %s", entity.getType()));
50+
}
51+
}
52+
53+
private T createInstance() throws InstantiationException, IllegalAccessException, InvocationTargetException {
54+
return (T) entity.getPersistenceConstructor().getConstructor().newInstance();
55+
}
56+
57+
private void setProperty(ResultSet rs, T t, PersistentProperty property) {
58+
59+
try {
60+
property.getSetter().invoke(t, rs.getObject(property.getName()));
61+
} catch (Exception e) {
62+
throw new RuntimeException(String.format("Couldn't set property %s.", property.getName()), e);
63+
}
64+
}
65+
}

src/main/java/org/springframework/data/jdbc/repository/SimpleJdbcRepository.java

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,17 @@
1616
package org.springframework.data.jdbc.repository;
1717

1818
import java.io.Serializable;
19+
import java.util.ArrayList;
1920
import java.util.HashMap;
21+
import java.util.List;
2022
import java.util.Map;
23+
import java.util.stream.Collectors;
2124
import javax.sql.DataSource;
25+
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
26+
import org.springframework.data.mapping.PersistentProperty;
27+
import org.springframework.data.mapping.PropertyHandler;
2228
import org.springframework.data.repository.CrudRepository;
23-
import org.springframework.data.repository.core.EntityInformation;
29+
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
2430
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
2531
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
2632

@@ -29,23 +35,25 @@
2935
*/
3036
public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRepository<T, ID> {
3137

32-
private final EntityInformation entityInformation;
38+
private final JdbcPersistentEntity<T> entity;
3339
private final NamedParameterJdbcOperations template;
3440

35-
public SimpleJdbcRepository(EntityInformation entityInformation, DataSource dataSource) {
36-
this.entityInformation = entityInformation;
41+
private final String findOneSql;
42+
private final String insertSql;
43+
44+
public SimpleJdbcRepository(JdbcPersistentEntity<T> entity, DataSource dataSource) {
45+
46+
this.entity = entity;
3747
this.template = new NamedParameterJdbcTemplate(dataSource);
48+
49+
findOneSql = createFindOneSelectSql();
50+
insertSql = createInsertSql();
3851
}
3952

4053
@Override
4154
public <S extends T> S save(S entity) {
42-
Map<String, Object> parameters = new HashMap<>();
43-
parameters.put("id", entityInformation.getId(entity));
44-
parameters.put("name", "blah blah");
4555

46-
template.update(
47-
"insert into dummyentity (id, name) values (:id, :name)",
48-
parameters);
56+
template.update(insertSql, getPropertyMap(entity));
4957

5058
return entity;
5159
}
@@ -57,7 +65,12 @@ public <S extends T> Iterable<S> save(Iterable<S> entities) {
5765

5866
@Override
5967
public T findOne(ID id) {
60-
return null;
68+
69+
return template.queryForObject(
70+
findOneSql,
71+
new MapSqlParameterSource("id", id),
72+
new EntityRowMapper<T>(entity)
73+
);
6174
}
6275

6376
@Override
@@ -99,4 +112,45 @@ public void delete(Iterable<? extends T> entities) {
99112
public void deleteAll() {
100113

101114
}
115+
116+
private String createFindOneSelectSql() {
117+
118+
String tableName = entity.getType().getSimpleName();
119+
String idColumn = entity.getIdProperty().getName();
120+
121+
return String.format("select * from %s where %s = :id", tableName, idColumn);
122+
}
123+
124+
private String createInsertSql() {
125+
126+
List<String> propertyNames = new ArrayList<>();
127+
entity.doWithProperties((PropertyHandler) persistentProperty -> propertyNames.add(persistentProperty.getName()));
128+
129+
String insertTemplate = "insert into %s (%s) values (%s)";
130+
131+
String tableName = entity.getType().getSimpleName();
132+
133+
String tableColumns = propertyNames.stream().collect(Collectors.joining(", "));
134+
String parameterNames = propertyNames.stream().collect(Collectors.joining(", :", ":", ""));
135+
136+
return String.format(insertTemplate, tableName, tableColumns, parameterNames);
137+
}
138+
139+
private <S extends T> Map<String, Object> getPropertyMap(final S entity) {
140+
141+
Map<String, Object> parameters = new HashMap<>();
142+
143+
this.entity.doWithProperties(new PropertyHandler() {
144+
@Override
145+
public void doWithPersistentProperty(PersistentProperty persistentProperty) {
146+
try {
147+
parameters.put(persistentProperty.getName(), persistentProperty.getGetter().invoke(entity));
148+
} catch (Exception e) {
149+
throw new RuntimeException(String.format("Couldn't get value of property %s", persistentProperty.getName()));
150+
}
151+
}
152+
});
153+
154+
return parameters;
155+
}
102156
}

src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,12 @@ public JdbcRepositoryFactory(DataSource dataSource) {
3939

4040
@Override
4141
public <T, ID extends Serializable> EntityInformation<T, ID> getEntityInformation(Class<T> aClass) {
42-
JdbcPersistentEntity<T> persistentEntity = (JdbcPersistentEntity<T>) context.getPersistentEntity(aClass);
43-
return new JdbcPersistentEntityInformation<T, ID>(persistentEntity);
42+
return new JdbcPersistentEntityInformation<T, ID>((JdbcPersistentEntity<T>) context.getPersistentEntity(aClass));
4443
}
4544

4645
@Override
4746
protected Object getTargetRepository(RepositoryInformation repositoryInformation) {
48-
return new SimpleJdbcRepository(getEntityInformation(repositoryInformation.getDomainType()), dataSource);
47+
return new SimpleJdbcRepository(context.getPersistentEntity(repositoryInformation.getDomainType()), dataSource);
4948
}
5049

5150
@Override

src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,18 @@ public class JdbcRepositoryIntegrationTests {
4848

4949
private final NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(db);
5050

51+
private final DummyEntityRepository repository = createRepository(db);
52+
53+
private DummyEntity entity = createDummyEntity();
54+
5155
@After
52-
public void afeter() {
56+
public void after() {
5357
db.shutdown();
5458
}
5559

5660

5761
@Test
5862
public void canSaveAnEntity() throws SQLException {
59-
DummyEntityRepository repository = createRepository();
60-
61-
DummyEntity entity = new DummyEntity();
62-
entity.setId(23L);
63-
entity.setName("Entity Name");
6463

6564
entity = repository.save(entity);
6665

@@ -74,9 +73,31 @@ public void canSaveAnEntity() throws SQLException {
7473
count);
7574
}
7675

77-
private DummyEntityRepository createRepository() throws SQLException {
78-
JdbcRepositoryFactory jdbcRepositoryFactory = new JdbcRepositoryFactory(db);
79-
return jdbcRepositoryFactory.getRepository(DummyEntityRepository.class);
76+
@Test
77+
public void canSaveAndLoadAnEntity() throws SQLException {
78+
79+
entity = repository.save(entity);
80+
81+
DummyEntity reloadedEntity = repository.findOne(entity.getId());
82+
83+
assertEquals(
84+
entity.getId(),
85+
reloadedEntity.getId());
86+
assertEquals(
87+
entity.getName(),
88+
reloadedEntity.getName());
89+
}
90+
91+
private static DummyEntityRepository createRepository(EmbeddedDatabase db) {
92+
return new JdbcRepositoryFactory(db).getRepository(DummyEntityRepository.class);
93+
}
94+
95+
96+
private static DummyEntity createDummyEntity() {
97+
DummyEntity entity = new DummyEntity();
98+
entity.setId(23L);
99+
entity.setName("Entity Name");
100+
return entity;
80101
}
81102

82103
private interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {

0 commit comments

Comments
 (0)