java - significa - ORM apoya clases inmutables
que significa inmutable en java (6)
¿Qué ORM admite un modelo de dominio de tipos inmutables ?
Me gustaría escribir clases como la siguiente (o el equivalente de Scala):
class A {
private final C c; //not mutable
A(B b) {
//init c
}
A doSomething(B b) {
// build a new A
}
}
El ORM tiene que inicializar el objeto con el constructor . Por lo tanto, es posible verificar invariantes en el constructor. El acceso predeterminado de constructor y campo / instalador para inicializar no es suficiente y complica la implementación de la clase.
Trabajar con colecciones debe ser compatible. Si se cambia una colección, debe crear una copia desde la perspectiva del usuario. (Renderizar el antiguo estado de recopilación obsoleta. Pero el código de usuario aún puede funcionar en (o al menos leerlo)). Al igual que las estructuras de datos persistentes funcionan.
Algunas palabras sobre la motivación. Supongamos que tiene un modelo de objeto de dominio estilo FP. Ahora quiere persistir esto en una base de datos. ¿A quién le haces eso? Desea hacer todo lo que pueda en un estilo funcional puro hasta que aparezca el efecto de lados malvados. Si su modelo de objetos de dominio no es inmutable, puede, por ejemplo, no compartir los objetos entre hilos. Tienes que copiar, guardar en caché o usar bloqueos. De modo que, a menos que su ORM admita tipos inmutables, estará limitado a su elección de solución.
AFAIK, no hay ORM para .NET que soporte esta función exactamente como lo desee. Pero puede echar un vistazo a BLTookit y LINQ to SQL; ambos proporcionan semántica de actualización por comparación y siempre devuelven nuevos objetos en la materialización. Eso es casi lo que necesita, pero no estoy seguro acerca de las colecciones allí.
Por cierto, ¿por qué necesita esta característica? Soy consciente de los lenguajes y beneficios funcionales puros de los objetos puramente imputables (por ejemplo, seguridad completa de los hilos). Pero en el caso de ORM, todas las cosas que haces con esos objetos finalmente se transforman en una secuencia de comandos SQL de todos modos. Admito que los beneficios de usar tales objetos son vaporosos aquí.
Aunque no es un ORM real , MyBatis puede hacer esto. No lo intenté sin embargo.
Hibernate tiene la anotación @Immutable
.
Puedes hacer esto con Ebean y OpenJPA (y creo que puedes hacer esto con Hibernate pero no estoy seguro). El ORM (Ebean / OpenJPA) generará un constructor predeterminado (suponiendo que el bean no tenga uno) y realmente establecerá los valores de los campos ''finales''. Esto suena un poco extraño, pero los campos finales no siempre son estrictamente definitivos, por decir.
SORM es un nuevo ORM de Scala que hace exactamente lo que usted desea. El siguiente código lo explicará mejor que cualquier palabra:
// Declare a model:
case class Artist ( name : String, genres : Set[Genre] )
case class Genre ( name : String )
// Initialize SORM, automatically generating schema:
import sorm._
object Db extends Instance (
entities = Set() + Entity[Artist]() + Entity[Genre](),
url = "jdbc:h2:mem:test"
)
// Store values in the db:
val metal = Db.save( Genre("Metal") )
val rock = Db.save( Genre("Rock") )
Db.save( Artist("Metallica", Set() + metal + rock) )
Db.save( Artist("Dire Straits", Set() + rock) )
// Retrieve values from the db:
val metallica = Db.query[Artist].whereEqual("name", "Metallica").fetchOne() // Option[Artist]
val rockArtists = Db.query[Artist].whereEqual("genres.name", "Rock").fetch() // Stream[Artist]
ACTUALIZACIÓN: JIRM un proyecto centrado en resolver este problema llamado JIRM : JIRM
Acabo de encontrar esta pregunta después de implementar la mía utilizando Spring JDBC y Jackson Object Mapper. Básicamente solo necesitaba un mínimo de asignación de objetos inmutables SQL <->.
En resumen, solo uso Springs RowMapper y ObjectMapper de Jackson para mapear objetos de ida y vuelta desde la base de datos. Uso las anotaciones JPA solo para metadatos (como el nombre de columna, etc.). Si la gente está interesada, lo limpiaré y lo pondré en github (en este momento solo está en el repositorio privado de mi startup).
Aquí hay una idea aproximada de cómo funciona aquí un ejemplo de frijol ( observe 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 parece el RowMapper (observe que delega principalmente a Springs ColumnMapRowMapper y luego usa el 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 solo tomé Spring JDBCTemplate y le di una envoltura fluida . Aquí hay unos 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 mapear cualquier consulta en POJO inmutables. Es decir, no lo necesita de 1 a 1 de entidad a tabla. También note el uso de los opcionales de Guava (ú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.