ORM支持不可变类 [英] ORM supporting immutable classes

查看:89
本文介绍了ORM支持不可变类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

哪个ORM支持不可变类型的域模型?



我想写类如下(或Scala等价物) :

  class A {
private final C c; //不可变

A(B b){
// init c
}

doSomething(B b){
//建立一个新的A
}
}

ORM必须用构造函数初始化对象。所以可以检查构造函数中的不变量。对intialize的缺省构造函数和field / setter访问是不够的,并且会使类的实现复杂化。应该支持使用集合。如果集合被更改,它应该从用户角度创建一个副本。 (渲染旧的集合状态陈旧,但用户代码仍然可以工作(或至少读取它)。)就像持久数据结构的工作。



关于动机的一些话。假设你有一个FP风格的域对象模型。现在你想坚持这个数据库。你是谁做的?你希望在纯粹的功能风格中尽可能地做到这一点,直到邪恶的双方效应进来。如果你的域对象模型不是不可变的,你可以例如不共享线程间的对象。你必须复制,缓存或使用锁。所以除非你的ORM支持不变类型,你可以在你选择的解决方案中进行约束。

创建了一个专注于解决此问题的项目,名称为 JIRM
< a href =https://github.com/agentgt/jirm =noreferrer> https://github.com/agentgt/jirm



我在使用 Spring JDBC Jackson Object Mapper。基本上我只需要一些最基本的SQL< - >不可变对象映射。

总之,我只是使用 Springs RowMapper 杰克逊的ObjectMapper 可以将对象映射回来,从数据库中取出。我使用JPA批注仅用于元数据(如列名等...)。 如果有人感兴趣,我会清理它并将其放在github上(现在它只在我的初创公司的私人回购站中)。

这里是一个粗略的想法是它如何在这里工作是一个bean示例(注意所有字段是最终的):

  //为简洁而跳过导入
public class TestBean {

@Id
private final String stringProp;
私人最终长期盈利;
@Column(name =timets)
私人决赛日历timeTS;
$ b @JsonCreator $ b $ public TestBean(
@JsonProperty(stringProp)String stringProp,
@JsonProperty(longProp)long longProp,
@ JsonProperty(timeTS)日历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;





这里 RowMapper 看起来像(注意它主要委托给Spring ColumnMapRowMapper,然后使用Jackson的objectmapper):

  public class SqlObjectRowMapper< T>实现RowMapper< T> {

private final SqlObjectDefinition< T>定义;
private final ColumnMapRowMapper mapRowMapper;
private final ObjectMapper objectMapper;

$ b public SqlObjectRowMapper(SqlObjectDefinition< T>定义,ObjectMapper objectMapper){
super();
this.definition =定义;
this.mapRowMapper = new SqlObjectMapRowMapper(definition);
this.objectMapper = objectMapper;

$ b $ public SqlObjectRowMapper(Class< T> k){
this(SqlObjectDefinition.fromClass(k),new ObjectMapper());


$ b @Override
public T mapRow(ResultSet rs,int rowNum)throws SQLException {
Map< String,Object> m = mapRowMapper.mapRow(rs,rowNum);
返回objectMapper.convertValue(m,definition.getObjectType());
}

}

现在我只用Spring JDBCTemplate给它一个流利包装。这里有一些例子:

  @Before 
public void setUp()抛出异常{
dao = new SqlObjectDao< TestBean>(新的JdbcTemplate(ds),TestBean.class);


$ b @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);


$ b @Test
public void testAdding()抛出异常{
//这将执行UPDATE test_bean SET longProp = longProp + 100
int i = dao.update()。add(longProp,100).update();
assertTrue(i> 0);

}

@Test
public void testRowMapper()抛出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 test_bean limit?)
.with(1)
.forObject(Crap.class);

可选<垃圾>缺少
= dao.query(select string_prop as test_bean where name 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)字符串名称){
super();
this.name = name;
}

public String getName(){
return name;
}

}

是将任何查询映射到不可变的POJO中。那就是你不需要它与实体的1对1表格。另请注意,使用 Guava的选择权(最后查询..向下滚动)。我非常讨厌ORM如何抛出异常或返回 null



让我知道如果你喜欢它,我会花时间把它放在github上(只能用postgresql来测试)。否则,使用上面的信息,您可以使用Spring JDBC轻松实现您自己的信息。我开始真正挖掘它,因为不可变对象更容易理解和思考。


Which ORM supports a domain model of immutable types?

I would like to write classes like the following (or the Scala equivalent):

class A {
  private final C c; //not mutable

  A(B b) {
     //init c
  }

  A doSomething(B b) {
     // build a new A
  }
}

The ORM has to initialized the object with the constructor. So it is possible to check invariants in the constructor. Default constructor and field/setter access to intialize is not sufficient and complicates the class' implementation.

Working with collections should be supported. If a collection is changed it should create a copy from the user perspective. (Rendering the old collection state stale. But user code can still work on (or at least read) it.) Much like the persistent data structures work.

Some words about the motivation. Suppose you have a FP-style domain object model. Now you want to persist this to a database. Who do you do that? You want to do as much as you can in a pure functional style until the evil sides effect come in. If your domain object model is not immutable you can for example not share the objects between threads. You have to copy, cache or use locks. So unless your ORM supports immutable types your constrainted in your choice of solution.

解决方案

UPDATE: I created a project focused on solving this problem called JIRM: https://github.com/agentgt/jirm

I just found this question after implementing my own using Spring JDBC and Jackson Object Mapper. Basically I just needed some bare minimum SQL <-> immutable object mapping.

In short I just use Springs RowMapper and Jackson's ObjectMapper to map Objects back and forth from the database. I use JPA annotations just for metadata (like column name etc...). If people are interested I will clean it up and put it on github (right now its only in my startup's private repo).

Here is a rough idea how it works here is an example bean (notice how all the fields are final):

//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;
    }

}

Here what the RowMapper looks like (notice it mainly delegats to Springs ColumnMapRowMapper and then uses Jackson's objectmapper):

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());
    }

}

Now I just took Spring JDBCTemplate and gave it a fluent wrapper. Here are some examples:

@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;
    }

}

Notice in the above how easy it is to map any query into immutable POJO's. That is you don't need it 1-to-1 of entity to table. Also notice the use of Guava's optionals (last query.. scroll down). I really hate how ORM's either throw exceptions or return null.

Let me know if you like it and I'll spend the time putting it on github (only teste with postgresql). Otherwise with the info above you can easily implement your own using Spring JDBC. I'm starting to really dig it because immutable objects are easier to understand and think about.

这篇关于ORM支持不可变类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆