Pergunta

(não relacionado ao controle de versão do esquema do banco de dados)

Os aplicativos que interfaces com bases de dados muitas vezes têm objetos de domínio que são compostos com dados de várias tabelas. Suponha que a aplicação fosse para apoiar o controle de versão, no sentido de CVS, para estes objetos de domínio.

Para alguns objeto de domínio arbitry, como você projetar um esquema de banco de dados para lidar com esse requisito? Qualquer experiência para partilhar?

Foi útil?

Solução

Pense cuidadosamente sobre os requisitos para revisões. Uma vez que sua base de código tem de rastreamento história difundida embutido no sistema operacional que vai ficar muito complexo. Insurance subscrição sistemas são particularmente ruins para isso, com esquemas muitas vezes correndo em excesso de 1000 tabelas. Consultas também tendem a ser bastante complexa e isso pode levar a problemas de desempenho.

Se o estado histórico é realmente necessário apenas para relatórios, considere a implementação de um sistema transacional 'estado atual', com uma estrutura de suspensão de data warehouse ao largo das costas para acompanhar a história. dimensões de mudança lenta são uma estrutura muito mais simples para o acompanhamento do estado histórico do que tentar incorporar um histórico ad-hoc mecanismo de rastreamento diretamente em seu sistema operacional.

Além disso, Changed Data Capture é mais simples para um sistema de 'estado atual', com mudanças sendo feito com os registros no lugar - as chaves primárias dos registros não mudar para que você não tem que coincidir com registros segurando versões diferentes da mesma entidade juntos. Um mecanismo CDC eficaz fará um processo de carregamento do armazém incrementais bastante leve e possível executar com bastante freqüência. Se você não precisa-se o rastreamento minuto de estado histórico (quase, mas não completamente, e oxímoro) esta pode ser uma solução eficaz com uma base de código muito mais simples do que um mecanismo de histórico de rastreamento completo construído diretamente no aplicativo.

Outras dicas

Uma técnica que eu usei para este em que o passado tem sido a de ter um conceito de "gerações" no banco de dados, cada mudança incrementa o número atual geração do banco de dados - se você usar a subversão, acho revisões. Cada registro tem 2 números de geração associados (2 colunas extras sobre as mesas) - a geração que os recordes começa a ser válido por, ea geração do ele deixa de ser válida. Se os dados é atualmente válido, o segundo número seria NULL ou algum outro marcador genérico.

Assim, para inserir no banco de dados:

  1. incrementar o número de geração
  2. Inserir os dados
  3. marca o tempo de vida que os dados com validade a partir de, e uma válida para NULL

Se você está atualizando alguns dados:

  1. marcar todos os dados que está prestes a ser modificado como válido para o número atual geração
  2. incrementar o número de geração
  3. inserir os novos dados com o número atual geração

exclusão é apenas uma questão de marcar os dados como terminando na geração atual.

Para obter uma versão específica dos dados, verificar qual geração você está atrás e olhar para os dados válidos entre estas versões geração.

Exemplo:

Criar uma pessoa.

|Name|D.O.B  |Telephone|From|To  |
|Fred|1 april|555-29384|1   |NULL|

Atualização tel não.

|Name|D.O.B  |Telephone|From|To  |
|Fred|1 april|555-29384|1   |1   |
|Fred|1 april|555-43534|2   |NULL|

Excluir fred:

|Name|D.O.B  |Telephone|From|To  |
|Fred|1 april|555-29384|1   |1   |
|Fred|1 april|555-43534|2   |2   |

Uma alternativa para o controle de versão estrita é dividir os dados em 2 mesas:. Corrente e da história

A tabela atual tem todos os dados em tempo real e tem os benefícios de todo o desempenho que você construir em. Quaisquer alterações primeiro escrever os dados atuais para a tabela associada a "história" junto com um marcador de data que diz quando mudou.

Se você estiver usando Hibernate JBoss Envers poderia ser uma opção. Você só tem a classes anotar com @Audited para manter sua história.

Você vai precisar de um registro mestre de uma tabela mestre que contém o comum de informações entre todas as versões.

Em seguida, cada criança usa a tabela registro mestre de ID + versão não como parte da chave primária.

Isso pode ser feito sem a tabela de mestre, mas na minha experiência, ele tenderá a tornar as instruções SQL a Messier muito.

Uma maneira infalível simples, é adicionar uma coluna de versão para suas tabelas e armazenar a versão do objeto e escolha a lógica da aplicação apropriada com base nesse número versão. Desta forma, você também terá compatibilidade com versões anteriores para pouco custo. Que é sempre bom

zodb + ZEO implementa um banco de dados baseado revisão com reversão completa para qualquer ponto de apoio tempo. Go check it.

Bad parte:. É Zope amarrado

Uma vez que um objeto é salvo em um banco de dados, podemos modificar esse objeto qualquer número de vezes direita, Se queremos saber quantos há de vezes que um objeto é modificado, então temos de aplicar este conceito de versão.

Sempre que usamos versões então hibernate inserções número de versão que zero, quando sempre objeto é salvo pela primeira vez no banco de dados. Mais tarde hibernar incrementos essa versão não por um automaticamente sempre que uma modificação é feita no objeto particular. Para utilizar este conceito de versionamento, precisamos das seguintes duas mudanças em nossa aplicação

Add one property of type int in our pojo class.

In hibernate mapping file, add an element called version soon after id element

Eu não tenho certeza se temos o mesmo problema, mas eu precisava de um grande número de 'proposta' alterações no conjunto de dados atual (com propostas acorrentados, ou seja, proposta sobre a proposta).

Pense ramificação no controle de origem, mas para tabelas de banco de dados.

Também queríamos um registro histórico, mas este foi o fator menos importante - a questão principal foi gerir propostas de mudança que pode pendurar em torno de 6 meses ou mais como o negócio refletia sobre aprovação de mudança e ficou pronto para a mudança real a ser implementado .

A idéia é que os usuários podem carregar uma mudança e começar a criar, editar, apagar o estado atual de dados sem realmente aplicar essas mudanças. Reverter quaisquer alterações que possa ter feito, ou cancelar toda a mudança.

A única maneira que eu fui capaz de conseguir isso é ter um conjunto de campos comuns em minhas tabelas de versão:

ID Raiz : Obrigatório - conjunto de uma vez para a chave primária quando a primeira versão de um registro é criado. Isto representa a chave primária através de todo o tempo e é copiado para cada versão do registro. Você deve considerar o ID Raiz quando nomeando colunas de relação (por exemplo. PARENT_ROOT_ID vez de PARENT_ID). Como o ID Root também é a chave primária da versão inicial, chaves estrangeiras podem ser criados contra a chave primária real -. A linha real desejado será determinado pelos filtros versão definida abaixo

Alterar ID : Obrigatório - cada registro é criado, atualizado, excluído por meio de uma mudança

copiado de ID : Nullable - nulo indica registro recém-criado, não-nulo indica que o registro ID esta linha foi clonada a partir de quando atualizados

em vigor a partir de Data / Hora : Nullable - nulo indica registro proposto, não-nulo indica quando o registro se tornou corrente. Infelizmente um índice exclusivo não pode ser colocado em Root ID / Eficaz De como pode haver vários valores nulos para qualquer ID Raiz. (A menos que você quer restringir-se a uma única alteração proposta por registro)

eficaz para Data / Hora : Nullable - nulo indica corrente / proposto, não-nulo indica quando se tornou histórica. Não é tecnicamente necessário, mas ajuda a acelerar as consultas para encontrar os dados atuais. Este campo pode ser corrompido por mão-edições, mas pode ser reconstruído a partir do vigor a partir de Data / Hora se isso ocorrer.

Flag Excluir : Boolean - definido para verdadeiro quando se propõe-se que o registro ser apagado ao se tornar atual. Quando exclusões são cometidos, a sua eficaz para Date / Time está definido para o mesmo valor que o vigor a partir de Data / Hora, filtrá-los para fora do conjunto de dados atual.

A consulta para obter o estado atual de dados de acordo com uma mudança seria;

SELECT * FROM table WHERE (CHANGE_ID IN :ChangeId OR (EFFECTIVE_FROM <= :Now AND (EFFECTIVE_TO IS NULL OR EFFECTIVE_TO > :Now) AND ROOT_ID NOT IN (SELECT ROOT_ID FROM table WHERE CHANGE_ID IN :ChangeId)))

(A filtragem de múltiplos mudança-on-a mudança é feita fora desta consulta).

A consulta para obter o estado atual dos dados em um ponto no tempo seria;

SELECT * FROM table WHERE EFFECTIVE_FROM <= :Now AND (EFFECTIVE_TO IS NULL OR EFFECTIVE_TO > :Now)

índices comuns criado em (ROOT_ID, EFFECTIVE_FROM), (EFFECTIVE_FROM, EFFECTIVE_TO) e (CHANGE_ID).

Se alguém souber uma solução melhor que eu gostaria de ouvir sobre isso.

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