O mapeamento funcional para relacional é mais fácil do que o mapeamento de objeto para relacional?

StackOverflow https://stackoverflow.com/questions/218190

Pergunta

O mapeamento objeto-relacional foi bem discutido, inclusive aqui.Tenho experiência com algumas abordagens e com as armadilhas e compromissos.A verdadeira resolução parece exigir mudanças no OO ou nos próprios modelos relacionais.

Se estiver usando uma linguagem funcional, o mesmo problema se apresenta?Parece-me que esses dois paradigmas deveriam se encaixar melhor do que OO e RDBMS.A ideia de pensar em conjuntos num RDBMS parece combinar-se com o paralelismo automático que as abordagens funcionais parecem prometer.

Alguém tem alguma opinião ou ideia interessante?Qual é a situação do setor?

Foi útil?

Solução

Os problemas difíceis de estender o banco de dados relacionais são transações estendidas, incompatibilidades do tipo dados, tradução automatizada de consulta e coisas como N+1 selecione que são problemas fundamentais de deixar o sistema relacional e - na minha opinião - não mudam alterando o paradigma de programação recepção.

Outras dicas

Qual é o objetivo de um ORM?

O principal objetivo do uso de um ORM é preencher entre o modelo em rede (orientação de objetos, gráficos etc.) e o modelo relacional. E a principal diferença entre os dois modelos é surpreendentemente simples. É se os pais apontam para os filhos (modelo em rede) ou crianças apontam para os pais (modelo relacional).

Com essa simplicidade em mente, acredito Não existe uma "incompatibilidade de impedância" entre os dois modelos. Os problemas que as pessoas costumam ter são puramente específicos de implementação e devem ser solucionáveis, se houver melhores protocolos de transferência de dados entre clientes e servidores.

Como o SQL pode abordar os problemas que temos com o ORMS?

Em particular, o terceiro manifesto tenta abordar as deficiências da linguagem SQL e da álgebra relacional, permitindo coleções aninhadas, que foram implementadas em uma variedade de bancos de dados, incluindo:

  • Oracle (provavelmente a implementação mais sofisticada)
  • PostgreSQL (até certo ponto)
  • Informix
  • SQL Server, MySQL, etc. (através da "emulação" via XML ou JSON)

Na minha opinião, se todos os bancos de dados implementaram o padrão SQL MULTISET() Operador (por exemplo, o Oracle), as pessoas não usariam mais ORMs para mapeamento (talvez ainda para persistência do gráfico de objetos), porque poderiam materializar coleções aninhadas diretamente de dentro dos bancos de dados, por exemplo, esta consulta:

SELECT actor_id, first_name, last_name,
  MULTISET (
    SELECT film_id, title
    FROM film AS f
    JOIN film_actor AS fa USING (film_id)
    WHERE fa.actor_id = a.actor_id
  ) AS films
FROM actor AS a

Realizaria todos os atores e seus filmes como uma coleção aninhada, em vez de um resultado de junção desnormalizado (onde os atores são repetidos para cada filme).

Paradigma funcional no lado do cliente

A questão de saber se uma linguagem de programação funcional no lado do cliente é mais adequada para as interações do banco de dados é realmente ortogonal. ORMS Ajuda com a persistência do gráfico de objetos; portanto, se o modelo do lado do cliente for um gráfico e você deseja que ele seja um gráfico, você precisará de um ORM, independentemente de você estar manipulando esse gráfico usando uma linguagem de programação funcional.

No entanto, como a orientação do objeto é menos idiomática em linguagens de programação funcional, é menos provável que você calça todos os itens de dados em um objeto. Para alguém que escreve SQL, projetando arbitrário tuplas é muito natural. O SQL abrange a digitação estrutural. Cada consulta SQL define seu próprio tipo de linha sem a necessidade de atribuir um nome anteriormente a ele. Isso ressoa muito bem com programadores funcionais, especialmente quando a inferência do tipo é sofisticada, no caso do qual você nunca pensará em mapear seu resultado SQL para algum objeto / classe definido anteriormente.

Um exemplo em Java usando Jooq Desta postagem do blog poderia ser:

// Higher order, SQL query producing function:
public static ResultQuery<Record2<String, String>> actors(Function<Actor, Condition> p) {
    return ctx.select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)
              .from(ACTOR)
              .where(p.apply(ACTOR)));
}

Essa abordagem leva a uma composicionalidade muito melhor das declarações SQL do que se a linguagem SQL fosse abstraída por alguns ORM, ou se a natureza natural "baseada em cordas" natural do SQL fosse usada. A função acima agora pode ser usada, por exemplo, assim:

// Get only actors whose first name starts with "A"
for (Record rec : actors(a -> a.FIRST_NAME.like("A%")))
    System.out.println(rec);

Abstração FRM sobre SQL

Alguns FRMs tentam abstrair sobre o idioma SQL, geralmente por esses motivos:

  • Eles afirmam que o SQL não pode ser composto o suficiente (Jooq refuta isso, é muito difícil acertar).
  • Eles afirmam que os usuários da API estão mais acostumados a APIs de coleção "nativas", então, por exemplo, JOIN é traduzido para flatMap() e WHERE é traduzido para filter(), etc.

Para responder sua pergunta

O FRM não é "mais fácil" do que o ORM, resolve um problema diferente. De fato, o FRM realmente não resolve nenhum problema, porque o SQL, sendo uma linguagem de programação declarativa (que não é tão diferente da programação funcional), é uma correspondência muito boa para outras linguagens funcionais de programação de clientes. Portanto, se alguma coisa, um FRM simplesmente preenche a lacuna entre o SQL, o DSL externo e o idioma do seu cliente.

(Eu trabalho para a empresa atrás Jooq, então esta resposta é tendenciosa)

Isso depende de suas necessidades

  1. Se você quiser focar nas estruturas de dados, use um ORM como JPA/Hibernate
  2. Se você quiser esclarecer os tratamentos, dê uma olhada nas bibliotecas FRM:QueryDSL ou Jooq
  3. Se você precisar ajustar suas solicitações SQL para bancos de dados específicos, use JDBC e solicitações SQL nativas

A força de várias tecnologias de "Mapeamento Relacional" é a portabilidade:você garante que seu aplicativo será executado na maioria dos bancos de dados ACID.Caso contrário, você lidará com diferenças entre vários dialetos SQL ao escrever manualmente as solicitações SQL.

É claro que você pode se restringir ao padrão SQL92 (e então fazer alguma programação funcional) ou pode reutilizar alguns conceitos de programação funcional com estruturas ORM

Os pontos fortes do ORM são construídos sobre um objeto de sessão que pode atuar como um gargalo:

  1. ele gerencia o ciclo de vida dos objetos enquanto a transação do banco de dados subjacente estiver em execução.
  2. ele mantém um mapeamento individual entre seus objetos Java e as linhas do banco de dados (e usa um cache interno para evitar objetos duplicados).
  3. ele detecta automaticamente atualizações de associação e objetos órfãos a serem excluídos
  4. ele lida com problemas simultâneos com bloqueio otimista ou pessimista.

No entanto, os seus pontos fortes são também os seus pontos fracos:

  1. A sessão deve ser capaz de comparar objetos, então você precisa implementar métodos equals/hashCode.Mas a igualdade de objetos deve estar enraizada em "Chaves de negócios" e não no ID do banco de dados (novos objetos transitórios não têm ID do banco de dados!).No entanto, alguns conceitos reificados não têm igualdade empresarial (uma operação, por exemplo).Uma solução alternativa comum depende de GUIDs que tendem a incomodar os administradores de banco de dados.

  2. A sessão deve espionar mudanças no relacionamento, mas suas regras de mapeamento forçam o uso de coleções inadequadas para os algoritmos de negócios.Às vezes você gostaria de usar um HashMap, mas o ORM exigirá que a chave seja outro "Objeto de Domínio Rico" em vez de outro leve ...Então você tem que implementar a igualdade de objetos no objeto de domínio rico atuando como uma chave...Mas não pode porque esse objeto não tem contrapartida no mundo dos negócios.Então você recorre a uma lista simples na qual precisa iterar (e da qual resultam problemas de desempenho).

  3. A API ORM às vezes é inadequada para uso no mundo real.Por exemplo, aplicações web do mundo real tentam impor o isolamento da sessão adicionando algumas cláusulas "WHERE" quando você busca dados...Então o "Session.get(id)" não é suficiente e você precisa recorrer a DSL mais complexo (HSQL, Criteria API) ou voltar ao SQL nativo

  4. Os objetos do banco de dados entram em conflito com outros objetos dedicados a outros frameworks (como frameworks OXM = Mapeamento de Objeto/XML).Por exemplo, se seus serviços REST usarem a biblioteca jackson para serializar um objeto de negócios.Mas este Jackson mapeia exatamente para um Hibernate One.Em seguida, você mescla ambos e um forte acoplamento entre sua API e seu banco de dados aparece Ou você deve implementar uma tradução e todo o código que você salvou do ORM é perdido lá...

Por outro lado, FRM é uma compensação entre "Mapeamento Relacional de Objetos" (ORM) e consultas SQL nativas (com JDBC)

A melhor forma de explicar as diferenças entre FRM e ORM consiste em adotar uma abordagem DDD.

  • O Mapeamento Objeto Relacional permite o uso de "Rich Domain Object", que são classes Java cujos estados são mutáveis ​​durante a transação do banco de dados
  • O Mapeamento Relacional Funcional depende de "Objetos de Domínio Pobres" que são imutáveis ​​(tanto que você precisa clonar um novo cada vez que quiser alterar seu conteúdo)

Ele libera as restrições colocadas na sessão ORM e depende na maioria das vezes de uma DSL sobre o SQL (portanto, a portabilidade não importa) Mas, por outro lado, você tem que olhar para os detalhes da transação, as questões de simultaneidade

List<Person> persons = queryFactory.selectFrom(person)
  .where(
    person.firstName.eq("John"),
    person.lastName.eq("Doe"))
  .fetch();

Eu acho que o mapeamento funcional para relacional deveria ser mais fácil de criar e usar do que OO para RDBMS.Contanto que você consulte apenas o banco de dados, claro.Eu realmente não vejo (ainda) como você poderia fazer atualizações de banco de dados sem efeitos colaterais de uma maneira agradável.

O principal problema que vejo é o desempenho.Os RDMS atuais não foram projetados para serem usados ​​com consultas funcionais e provavelmente se comportarão mal em alguns casos.

Eu não fiz o mapeamento funcional-relacional, por si só, mas usei técnicas de programação funcional para acelerar o acesso a um RDBMS.

É bastante comum começar com um conjunto de dados, fazer algum computação complexa e armazenar os resultados, onde os resultados são um subconjunto do original com valores adicionais, por exemplo. A abordagem imperativa determina que você armazena seu conjunto de dados inicial com colunas nulas extras, faça seu cálculo e atualize os registros com os valores computados.

Parece razoável. Mas o problema com isso é que pode ficar muito lento. Se o seu cálculo exigir outra instrução SQL além da própria consulta de atualização, ou mesmo precisar ser feita no código do aplicativo, você literalmente precisa (re) pesquisar os registros que está mudando após o cálculo para armazenar seus resultados nas linhas certas .

Você pode contornar isso simplesmente criando uma nova tabela para obter resultados. Dessa forma, você sempre pode inserir em vez de atualizar. Você acaba tendo outra mesa, duplicando as chaves, mas não precisa mais desperdiçar espaço em colunas armazenando nulo - você armazena apenas o que tem. Você participa dos seus resultados em sua seleção final.

Eu (AB) usei um RDBMS dessa maneira e acabei escrevendo declarações SQL que pareciam principalmente com isso ...

create table temp_foo_1 as select ...;
create table temp_foo_2 as select ...;
...
create table foo_results as
  select * from temp_foo_n inner join temp_foo_1 ... inner join temp_foo_2 ...;

O que isso está fazendo é criar um monte de ligações imutáveis. O bom, porém, é que você pode trabalhar em conjuntos inteiros de uma só vez. Meio que lembra idiomas que permitem trabalhar com matrizes, como o MATLAB.

Eu imagino que isso também permitiria o paralelismo muito mais fácil.

Uma vantagem extra é que os tipos de colunas para tabelas criados dessa maneira não precisam ser especificados porque são deduzidos das colunas que são selecionadas.

Eu acho que, como Sam mencionou, se o banco de dados deve ser atualizado, os mesmos problemas de simultaneidade devem ser enfrentados como o OO World. A natureza funcional do programa talvez seja um pouco mais problemática que a natureza do objeto devido ao estado de dados, transações etc. do RDBMS.

Mas para a leitura, a linguagem funcional pode ser mais natural com alguns domínios de problemas (como parece ser independentemente do banco de dados)

O mapeamento funcional <-> rdbms não deve ter grandes diferenças para os mapeamentos de OO <-> rdmbs. Mas acho que isso depende muito de que tipo de tipos de dados você deseja usar, se deseja desenvolver um programa com um novo esquema de banco de dados ou fazer algo contra um esquema de banco de dados herdado, etc.

As buscas preguiçosas etc para associações, por exemplo, provavelmente poderiam ser implementadas muito bem com alguns conceitos relacionados à avaliação preguiçosa. (Mesmo que eles possam ser feitos muito bem com oo também)

Editar: com um pouco de Google, encontrei Haskelldb (Biblioteca SQL para Haskell) - Isso pode valer a pena tentar?

Bancos de dados e programação funcional podem ser fundidos.

por exemplo:

Clojure é uma linguagem de programação funcional baseada na teoria do banco de dados relacional.

               Clojure -> DBMS, Super Foxpro
                   STM -> Transaction,MVCC
Persistent Collections -> db, table, col
              hash-map -> indexed data
                 Watch -> trigger, log
                  Spec -> constraint
              Core API -> SQL, Built-in function
              function -> Stored Procedure
             Meta Data -> System Table

NOTA: No mais recente Spec2, o SPEC é mais parecido com o RMDB. Vejo: Spec-alpha2 Wiki: esquema e seleção

Advogado: Construindo um modelo de dados relacional sobre o hash-map para obter uma combinação de vantagens NoSQL e RMDB. Esta é na verdade uma implementação reversa do POSGTRESQL.

Digitação de pato: se parece um pato e charriga como um pato, deve ser um pato.

Se o modelo de dados de Clojure como um RMDB, as instalações de Clojure como um RMDB e a manipulação de dados de Clojure como um RMDB, o Clojure deve ser um RMDB.

Clojure é uma linguagem de programação funcional baseada na teoria do banco de dados relacional

Tudo é rmdb

Implementar modelo de dados relacional e programação com base no hash-map (NOSQL)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top