ACTUALIZACIÓN: he creado un proyecto centrado en la solución de este problema llamado JIRM: https://github.com/agentgt/jirm
Acabo de encontrar esta pregunta después de implementar el uso de mi propia Spring JDBC y Jackson asignador de objetos. Básicamente solo necesitaba un mínimo de SQL < -> asignación de objetos inmutables.
En resumen sólo tiene que utilizar Springs RowMapper y Jackson's ObjectMapper a objetos de mapa de ida y vuelta desde la base de datos. Uso las anotaciones JPA solo para metadatos (como el nombre de columna, etc.). Si las personas están interesadas, lo limpiaré y lo pondré en github (en este momento solo está en el repositorio privado de mi startup).
Aquí está una idea aproximada de cómo funciona aquí es un bean de ejemplo (aviso cómo todos los campos son finales):
//skip imports for brevity
public class TestBean {
@Id
private final String stringProp;
private final long longProp;
@Column(name="timets")
private final Calendar timeTS;
@JsonCreator
public TestBean(
@JsonProperty("stringProp") String stringProp,
@JsonProperty("longProp") long longProp,
@JsonProperty("timeTS") Calendar timeTS) {
super();
this.stringProp = stringProp;
this.longProp = longProp;
this.timeTS = timeTS;
}
public String getStringProp() {
return stringProp;
}
public long getLongProp() {
return longProp;
}
public Calendar getTimeTS() {
return timeTS;
}
}
Aquí lo que el RowMapper parece (lo nota principalmente delegats en Springs ColumnMapRowMapper y luego utiliza objectmapper de Jackson):
public class SqlObjectRowMapper<T> implements RowMapper<T> {
private final SqlObjectDefinition<T> definition;
private final ColumnMapRowMapper mapRowMapper;
private final ObjectMapper objectMapper;
public SqlObjectRowMapper(SqlObjectDefinition<T> definition, ObjectMapper objectMapper) {
super();
this.definition = definition;
this.mapRowMapper = new SqlObjectMapRowMapper(definition);
this.objectMapper = objectMapper;
}
public SqlObjectRowMapper(Class<T> k) {
this(SqlObjectDefinition.fromClass(k), new ObjectMapper());
}
@Override
public T mapRow(ResultSet rs, int rowNum) throws SQLException {
Map<String, Object> m = mapRowMapper.mapRow(rs, rowNum);
return objectMapper.convertValue(m, definition.getObjectType());
}
}
Ahora sólo tomó primavera JdbcTemplate y le dio un fluidez envoltura. Aquí hay algunos ejemplos:
@Before
public void setUp() throws Exception {
dao = new SqlObjectDao<TestBean>(new JdbcTemplate(ds), TestBean.class);
}
@Test
public void testAll() throws Exception {
TestBean t = new TestBean(IdUtils.generateRandomUUIDString(), 2L, Calendar.getInstance());
dao.insert(t);
List<TestBean> list = dao.queryForListByFilter("stringProp", "hello");
List<TestBean> otherList = dao.select().where("stringProp", "hello").forList();
assertEquals(list, otherList);
long count = dao.select().forCount();
assertTrue(count > 0);
TestBean newT = new TestBean(t.getStringProp(), 50, Calendar.getInstance());
dao.update(newT);
TestBean reloaded = dao.reload(newT);
assertTrue(reloaded != newT);
assertTrue(reloaded.getStringProp().equals(newT.getStringProp()));
assertNotNull(list);
}
@Test
public void testAdding() throws Exception {
//This will do a UPDATE test_bean SET longProp = longProp + 100
int i = dao.update().add("longProp", 100).update();
assertTrue(i > 0);
}
@Test
public void testRowMapper() throws Exception {
List<Crap> craps = dao.query("select string_prop as name from test_bean limit ?", Crap.class, 2);
System.out.println(craps.get(0).getName());
craps = dao.query("select string_prop as name from test_bean limit ?")
.with(2)
.forList(Crap.class);
Crap c = dao.query("select string_prop as name from test_bean limit ?")
.with(1)
.forObject(Crap.class);
Optional<Crap> absent
= dao.query("select string_prop as name from test_bean where string_prop = ? limit ?")
.with("never")
.with(1)
.forOptional(Crap.class);
assertTrue(! absent.isPresent());
}
public static class Crap {
private final String name;
@JsonCreator
public Crap(@JsonProperty ("name") String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
}
Observe en lo anterior lo fácil que es asignar cualquier consulta a POJOs inmutables. Es decir, no lo necesita de 1 a 1 de entidad a tabla. También observe el uso de Guava's optionals (última consulta ... desplácese hacia abajo). Realmente odio cómo ORM arroja excepciones o devuelve null
.
Avíseme si le gusta y pasaré el tiempo poniéndolo en github (solo teste con postgresql). De lo contrario, con la información anterior, puede implementar fácilmente su propio uso de Spring JDBC. Estoy comenzando a cavar realmente porque los objetos inmutables son más fáciles de entender y pensar.
Por lo que sé, Hibernate no admite los campos finales y la "inyección de constructor" de valores. –
¿necesitas 'final'? Puedes limitarlo con la falta de setters. – Bozho
No sería inmutable de lo contrario. No me gustan los marcos para restringir mi elección de las características del lenguaje. –