ORM supporting immutable classes

前端 未结 6 802
暖寄归人
暖寄归人 2021-02-03 22:15

Which ORM supports a domain model of immutable types?

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

class          


        
6条回答
  •  轻奢々
    轻奢々 (楼主)
    2021-02-03 23:04

    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 implements RowMapper {
    
        private final SqlObjectDefinition definition;
        private final ColumnMapRowMapper mapRowMapper;
        private final ObjectMapper objectMapper;
    
    
        public SqlObjectRowMapper(SqlObjectDefinition definition, ObjectMapper objectMapper) {
            super();
            this.definition = definition;
            this.mapRowMapper = new SqlObjectMapRowMapper(definition);
            this.objectMapper = objectMapper;
        }
    
        public SqlObjectRowMapper(Class k) {
            this(SqlObjectDefinition.fromClass(k), new ObjectMapper());
        }
    
    
        @Override
        public T mapRow(ResultSet rs, int rowNum) throws SQLException {
            Map 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(new JdbcTemplate(ds), TestBean.class);
    
    }
    
    @Test
    public void testAll() throws Exception {
        TestBean t = new TestBean(IdUtils.generateRandomUUIDString(), 2L, Calendar.getInstance());
        dao.insert(t);
        List list = dao.queryForListByFilter("stringProp", "hello");
        List 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 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 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.

提交回复
热议问题