ORM поддерживает неизменяемые классы - PullRequest
28 голосов
/ 23 апреля 2010

Какой ORM поддерживает модель предметной области неизменных типов?

Я хотел бы написать классы, подобные следующим (или эквивалент Scala):

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

  A(B b) {
     //init c
  }

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

ORM должен инициализировать объект конструктором .Таким образом, можно проверить инварианты в конструкторе.Доступ по умолчанию для конструктора и поля / установщика для инициализации недостаточен и усложняет реализацию класса.

Должна поддерживаться работа с коллекциями .Если коллекция изменена, она должна создать копию с точки зрения пользователя.(Рендеринг старого состояния коллекции устарел. Но пользовательский код все еще может работать (или, по крайней мере, читать).) Очень похоже на постоянные структуры данных работа.

Несколько слов о мотивацииПредположим, у вас есть объектная модель домена в стиле FP.Теперь вы хотите сохранить это в базе данных.Кто ты это делаешь?Вы хотите делать как можно больше в чистом функциональном стиле, пока не проявится эффект злых сторон. Если ваша объектная модель домена не является неизменной, вы можете, например, не делить объекты между потоками.Вы должны копировать, кэшировать или использовать блокировки.Поэтому, если ваш ORM не поддерживает неизменяемые типы, вы ограничены в выборе решения.

Ответы [ 6 ]

7 голосов
/ 06 сентября 2012

ОБНОВЛЕНИЕ: Я создал проект, направленный на решение этой проблемы, под названием JIRM : https://github.com/agentgt/jirm

Я только что нашел этот вопрос после того, как реализовал свой собственный, используя Spring JDBC и Jackson Object Mapper. По сути, мне просто нужно было минимальное неизменяемое сопоставление объектов SQL <->.

Короче говоря, я просто использую Springs RowMapper и ObjectMapper Джексона для отображения объектов назад и вперед из базы данных. Я использую аннотации JPA только для метаданных (например, имя столбца и т. Д.). Если людям будет интересно, я уберу это и положу на github (сейчас это только в личном репозитории моего запуска).

Вот примерное представление о том, как это работает, приведен пример bean-компонента ( обратите внимание, как все поля являются окончательными ):

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

}

Вот как выглядит RowMapper (обратите внимание, что он в основном делегирует Springs ColumnMapRowMapper, а затем использует объектную карту Джексона):

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

}

Теперь я просто взял Spring JDBCTemplate и дал ему свободно оболочку. Вот несколько примеров:

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

}

Обратите внимание на то, как легко сопоставить любой запрос в неизменяемые POJO. То есть вам не нужно это 1 к 1 от сущности к столу. Также обратите внимание на использование опций Guava (последний запрос ... прокрутите вниз). Я действительно ненавижу, как ORM либо генерирует исключения, либо возвращает null.

Дайте мне знать, если вам это нравится, и я потрачу время на то, чтобы положить его на github (только teste с postgresql). В противном случае, используя приведенную выше информацию, вы можете легко реализовать свою собственную, используя Spring JDBC. Я начинаю действительно копать его, потому что неизменяемые объекты легче понять и подумать.

4 голосов
/ 23 апреля 2010

Hibernate имеет аннотацию @Immutable.

И вот руководство .

3 голосов
/ 19 июля 2010

Хотя это не реальная ORM, MyBatis может сделать это.Хотя я не пробовал.

http://mybatis.org/java.html

0 голосов
/ 20 ноября 2012

SORM - это новый Scala ORM, который делает именно то, что вы хотите.Код ниже объяснит это лучше, чем любые слова:

// 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]
0 голосов
/ 26 июля 2010

Вы можете сделать это с Ebean и OpenJPA (и я думаю, что вы можете сделать это с Hibernate, но не уверен). ORM (Ebean / OpenJPA) сгенерирует конструктор по умолчанию (при условии, что у компонента нет такового) и фактически установит значения полей 'final'. Это звучит немного странно, но окончательные поля не всегда являются окончательными, скажем так.

0 голосов
/ 23 апреля 2010

AFAIK, ORM для .NET не поддерживают эту функцию именно так, как вы хотите.Но вы можете взглянуть на BLTookit и LINQ to SQL - оба предоставляют семантику обновления путем сравнения и всегда возвращают новые объекты при материализации.Это почти то, что вам нужно, но я не уверен насчет коллекций там.

Кстати, зачем вам эта функция?Я знаю о чисто функциональных языках и преимуществах чисто неизменяемых объектов (например, полная безопасность потоков).Но в случае с ORM все, что вы делаете с такими объектами, в конечном итоге все равно преобразуется в последовательность команд SQL.Поэтому я признаю, что преимущества использования таких объектов здесь невелики.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...