Browse Source

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.

Original pull request: #5.
pull/6/merge
Jens Schauder 9 years ago committed by Oliver Gierke
parent
commit
38a91d97a0
  1. 65
      src/main/java/org/springframework/data/jdbc/repository/EntityRowMapper.java
  2. 76
      src/main/java/org/springframework/data/jdbc/repository/SimpleJdbcRepository.java
  3. 5
      src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java
  4. 39
      src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java

65
src/main/java/org/springframework/data/jdbc/repository/EntityRowMapper.java

@ -0,0 +1,65 @@
/*
* Copyright 2017 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.repository;
import java.lang.reflect.InvocationTargetException;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PropertyHandler;
/**
* @author Jens Schauder
*/
class EntityRowMapper<T> implements org.springframework.jdbc.core.RowMapper<T> {
private final PersistentEntity<T, ?> entity;
EntityRowMapper(PersistentEntity<T, ?> entity) {
this.entity = entity;
}
@Override
public T mapRow(ResultSet rs, int rowNum) throws SQLException {
try {
T t = createInstance();
entity.doWithProperties((PropertyHandler) property -> {
setProperty(rs, t, property);
});
return t;
} catch (Exception e) {
throw new RuntimeException(String.format("Could not instantiate %s", entity.getType()));
}
}
private T createInstance() throws InstantiationException, IllegalAccessException, InvocationTargetException {
return (T) entity.getPersistenceConstructor().getConstructor().newInstance();
}
private void setProperty(ResultSet rs, T t, PersistentProperty property) {
try {
property.getSetter().invoke(t, rs.getObject(property.getName()));
} catch (Exception e) {
throw new RuntimeException(String.format("Couldn't set property %s.", property.getName()), e);
}
}
}

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

@ -16,11 +16,17 @@
package org.springframework.data.jdbc.repository; package org.springframework.data.jdbc.repository;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.core.EntityInformation; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
@ -29,23 +35,25 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
*/ */
public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRepository<T, ID> { public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRepository<T, ID> {
private final EntityInformation entityInformation; private final JdbcPersistentEntity<T> entity;
private final NamedParameterJdbcOperations template; private final NamedParameterJdbcOperations template;
public SimpleJdbcRepository(EntityInformation entityInformation, DataSource dataSource) { private final String findOneSql;
this.entityInformation = entityInformation; private final String insertSql;
public SimpleJdbcRepository(JdbcPersistentEntity<T> entity, DataSource dataSource) {
this.entity = entity;
this.template = new NamedParameterJdbcTemplate(dataSource); this.template = new NamedParameterJdbcTemplate(dataSource);
findOneSql = createFindOneSelectSql();
insertSql = createInsertSql();
} }
@Override @Override
public <S extends T> S save(S entity) { public <S extends T> S save(S entity) {
Map<String, Object> parameters = new HashMap<>();
parameters.put("id", entityInformation.getId(entity));
parameters.put("name", "blah blah");
template.update( template.update(insertSql, getPropertyMap(entity));
"insert into dummyentity (id, name) values (:id, :name)",
parameters);
return entity; return entity;
} }
@ -57,7 +65,12 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
@Override @Override
public T findOne(ID id) { public T findOne(ID id) {
return null;
return template.queryForObject(
findOneSql,
new MapSqlParameterSource("id", id),
new EntityRowMapper<T>(entity)
);
} }
@Override @Override
@ -99,4 +112,45 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
public void deleteAll() { public void deleteAll() {
} }
private String createFindOneSelectSql() {
String tableName = entity.getType().getSimpleName();
String idColumn = entity.getIdProperty().getName();
return String.format("select * from %s where %s = :id", tableName, idColumn);
}
private String createInsertSql() {
List<String> propertyNames = new ArrayList<>();
entity.doWithProperties((PropertyHandler) persistentProperty -> propertyNames.add(persistentProperty.getName()));
String insertTemplate = "insert into %s (%s) values (%s)";
String tableName = entity.getType().getSimpleName();
String tableColumns = propertyNames.stream().collect(Collectors.joining(", "));
String parameterNames = propertyNames.stream().collect(Collectors.joining(", :", ":", ""));
return String.format(insertTemplate, tableName, tableColumns, parameterNames);
}
private <S extends T> Map<String, Object> getPropertyMap(final S entity) {
Map<String, Object> parameters = new HashMap<>();
this.entity.doWithProperties(new PropertyHandler() {
@Override
public void doWithPersistentProperty(PersistentProperty persistentProperty) {
try {
parameters.put(persistentProperty.getName(), persistentProperty.getGetter().invoke(entity));
} catch (Exception e) {
throw new RuntimeException(String.format("Couldn't get value of property %s", persistentProperty.getName()));
}
}
});
return parameters;
}
} }

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

@ -39,13 +39,12 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport {
@Override @Override
public <T, ID extends Serializable> EntityInformation<T, ID> getEntityInformation(Class<T> aClass) { public <T, ID extends Serializable> EntityInformation<T, ID> getEntityInformation(Class<T> aClass) {
JdbcPersistentEntity<T> persistentEntity = (JdbcPersistentEntity<T>) context.getPersistentEntity(aClass); return new JdbcPersistentEntityInformation<T, ID>((JdbcPersistentEntity<T>) context.getPersistentEntity(aClass));
return new JdbcPersistentEntityInformation<T, ID>(persistentEntity);
} }
@Override @Override
protected Object getTargetRepository(RepositoryInformation repositoryInformation) { protected Object getTargetRepository(RepositoryInformation repositoryInformation) {
return new SimpleJdbcRepository(getEntityInformation(repositoryInformation.getDomainType()), dataSource); return new SimpleJdbcRepository(context.getPersistentEntity(repositoryInformation.getDomainType()), dataSource);
} }
@Override @Override

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

@ -48,19 +48,18 @@ public class JdbcRepositoryIntegrationTests {
private final NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(db); private final NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(db);
private final DummyEntityRepository repository = createRepository(db);
private DummyEntity entity = createDummyEntity();
@After @After
public void afeter() { public void after() {
db.shutdown(); db.shutdown();
} }
@Test @Test
public void canSaveAnEntity() throws SQLException { public void canSaveAnEntity() throws SQLException {
DummyEntityRepository repository = createRepository();
DummyEntity entity = new DummyEntity();
entity.setId(23L);
entity.setName("Entity Name");
entity = repository.save(entity); entity = repository.save(entity);
@ -74,9 +73,31 @@ public class JdbcRepositoryIntegrationTests {
count); count);
} }
private DummyEntityRepository createRepository() throws SQLException { @Test
JdbcRepositoryFactory jdbcRepositoryFactory = new JdbcRepositoryFactory(db); public void canSaveAndLoadAnEntity() throws SQLException {
return jdbcRepositoryFactory.getRepository(DummyEntityRepository.class);
entity = repository.save(entity);
DummyEntity reloadedEntity = repository.findOne(entity.getId());
assertEquals(
entity.getId(),
reloadedEntity.getId());
assertEquals(
entity.getName(),
reloadedEntity.getName());
}
private static DummyEntityRepository createRepository(EmbeddedDatabase db) {
return new JdbcRepositoryFactory(db).getRepository(DummyEntityRepository.class);
}
private static DummyEntity createDummyEntity() {
DummyEntity entity = new DummyEntity();
entity.setId(23L);
entity.setName("Entity Name");
return entity;
} }
private interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> { private interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {

Loading…
Cancel
Save