Tabela com chave composta e JoinColumn (Hibernate)

Boa tarde a todos,

Antes de inserir o post fiz pesquisas e vários teste e não cheguei ao resultado esperado.

Toda ajuda será bem vinda. Desde já agradeço.

Caso: Incluir registros e atualizar dados da tabela Pai(principal) para tabela filho considerando as seguintes situações:

Processo Contas a Pagar:

  • 1 Tabela principal - e501cpa - Registro do contas a pagar.
  • 2 Tabela secundária - e501mcp - Registro de movimentos;

Na tabela principal a chave é composta por 3 campos. Numero do Título, Tipo do Título e Código do Fornecedor. Essa tabela será a principal, terá as informações do título (boleto) como data vencimento, situação do título, valor, numero da nota fiscal e assim por diante.

Na tabela secundária a chave também é composta por 4 campos. Numero do Título, Tipo do Título e Código do Fornecedor e Sequencia do Movimento (Chave composta da tabela E501TCP + Sequencia Movimento) . Nesta tabela registra os movimentos que podem ocorrer para o título até a sua liquidação. Quero dizer que um título pode ser pagos em varias formas de pagamento e com datas diferentes (isso é o que acontece no mundo real). Para isso temos essa tabela, tipo para registrar o histórico de pagamentos parciais.

Eventos:
Inserir titulo 1 registro: ao registrar o primeiro registro (título contas a pagar) a tabela principal deverá gravar os dados na tabela secundária(filha). (está fazendo mais creio que não seja a melhor prática).
Alterar título 1 registro: se caso alterar a data de vencimento ou valor deverá ser feito apenas para o primeiro registro desde que não tenha algum movimento na tabela E501MCP que a sequencia seja maior que o primeiro registro.
Excluir título 1 registro: se excluir o título deverá excluir o titulo da tabela E501MCP desde que não tenha movimentos onde a sequencia do movimento seja maior que o primeiro registro.

Inserir Registros parciais de baixa: quando realizar um movimento sequencia 2 (exemplo baixa parcial do titulo, isto é pagamento não integral , realizada por outro processo) a tabela E501TCP não deve restringir a tabela E501MCP.

Então, pessoal, alguém já fez algo semelhante e que poderia dar uma ajuda?

Não sei se a melhor opção é utilizar JoinTable com JoinColumns ou se continuo utilizando o mapeamento ManyToOne persistindo campos chaves.

Código.

Tabela Principal (e501tcp)

@Entity
@Table(name="e501tcp")
@IdClass(value= TituloCPAId.class)
public class TituloCPA implements Serializable {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = -194728923344243052L;
	/*	
	 @EmbeddedId
	private TituloCPAId tituloCPAId;
	*/
	@Id
	private String numTitulo;
	@Id
	private String tipoTitulo;
	@Id
	private Integer codigoFornecedor;

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name="codfor", referencedColumnName="codfor", insertable=false, updatable = false,  foreignKey=@ForeignKey(name="fk_e501tcp_e095for"))
	private Fornecedor fornecedor;	
	@Column(name="codtns", length=5)

Tabela Secundária (e501mcp)

@Entity
@Table(name="e501mcp")
@IdClass(value= TituloCPAMovtoId.class)
public class TituloCPAMovto implements Serializable {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 3608713509129343024L;

	@Id
	private String numTitulo;
	@Id
	private String tipoTitulo;
	@Id
	private Integer codigoFornecedor;
	@Id
	private int 	seqMovimento;
	
	@ManyToOne(fetch=FetchType.LAZY)
	@JoinColumn(name="codcfn", referencedColumnName="codcfn", foreignKey=@ForeignKey(name="fk_e501tcp_e000pfn"))
	private PlanoFinanceiro planoFinanceiro;
	@ManyToOne(fetch=FetchType.LAZY)
	@JoinColumn(name="codccu", referencedColumnName="codccu", foreignKey=@ForeignKey(name="fk_e501tcp_e000ccu"))
	private CentroCusto centroCusto;
	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name="codfor", referencedColumnName="codfor", insertable=false, updatable = false,  foreignKey=@ForeignKey(name="fk_e501tcp_e095for"))
	private Fornecedor fornecedor;
	@ManyToOne
	@JoinColumn(name="codemp", referencedColumnName="codemp", foreignKey=@ForeignKey(name="fk_e501mcp_e000emp"))
	private Empresa empresa;
	@ManyToOne
	@JoinColumn(name="codigo", referencedColumnName="codigo", foreignKey=@ForeignKey(name="fk_e501mcp_usuario"))
	private Usuario usuario;	
	
	@Temporal(TemporalType.DATE)
	@Column(name="datmov")
	private Date 	dataMovimento;
	@Column(name="codtns", length=5)
	private String  tnsMovimento;
	@Column(name="obsmov", length= 160)
	private String  obsMovimento;
	@Temporal(TemporalType.DATE)

Método Salvar

	public void salvar(){
		//var p msg
		String msgNumTitCPA = this.tituloCPA.getNumTitulo();
		String msgTipoTitulo = this.tituloCPA.getTipoTitulo();
		String msgForTitCPA = this.tituloCPA.getFornecedor().getNomeFornecedor();
		BigDecimal msgVlrTitCPA =  this.tituloCPA.getValorOriginal();
		
		FacesContext context = FacesContext.getCurrentInstance();
		
		Date dataAtual = new Date(System.currentTimeMillis());
		Date vDtaEntrada = tituloCPA.getDataEntrada();			// data de entrada do título
		Date vDtaEmissao = tituloCPA.getDataEmissao();			// data de emisso do título
		Date vDtaVencimento = tituloCPA.getDataVencimento();		// data de vencimento titulo
		Integer vCtaFin = this.tituloCPA.getPlanoFinanceiro().getCodCtaFinanc();
		Integer vCtaCcu = this.tituloCPA.getCentroCusto().getCodCentroCusto();
		
		
		if(vCtaFin == 37){
			  FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN,"Campo Class.Despesas. Falta classificar o tipo de depesa do título!", null));
				return ;			
		}

		if(vCtaCcu == 11){
			  FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN,"Campo Aprop.Custos. Falta definir o departamento que absorve o custo!", null));
				return ;			
		}		

		
		/* Validação do campo Data entrada
		 * 		A data de entrad não pode ser superior à data do dia atual
		 * */
		if(vDtaEntrada.after(dataAtual)){
		/*	FacesMessage facesMessage = new FacesMessage( "A data de entrada do título deve ser inferior a data do dia atual!");
			context.addMessage(null, facesMessage);*/
		  FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN,"Campo Data entrada. O título não pode ser registrado em data futura ao dia atual. Processo não existe!", null));
			return ;
		}
		
		/* Validação do campo Data emissão
		 * 		A data de emissão não pode ser superior à data de entrada do título
		 * */
		if(vDtaEmissao.after(vDtaEntrada)){
			FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN,"Campo Data emissão. O título não pode ser registrado com a data de emissão superior a data de entrada!", null));
			return ;
		}
		/* Validação do campo Data vencimento
		 * 		A data de vencimento não pode ser inferior à data de emissão do título.
		 * */		
		if(vDtaVencimento.before(vDtaEmissao)){
			FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN,"Campo Data vencimento. O título não pode ser registrado com a data de vencimento inferior a data de emissão do título!",null));
			return ;
		}
			
		
		//consiste chaves;
		this.tituloCPA.setEmpresa(this.contextoBean.getUsuarioLogado().getEmpresa());
		this.tituloCPA.setUsuario(this.contextoBean.getUsuarioLogado());
		this.tituloCPA.setCodigoFornecedor(this.tituloCPA.getFornecedor().getCodigoFornecedor());
		this.tituloCPA.setNumTitulo(this.tituloCPA.getNumTitulo());
		this.tituloCPA.setTipoTitulo(this.tituloCPA.getTipoTitulo());
		
		//consiste valor padrao
		this.tituloCPA.setTnsTitulo("90500");
		this.tituloCPA.setSitTitulo("AB");
		this.tituloCPA.setValorAberto(this.tituloCPA.getValorOriginal());
		
		
		
		
		//salvar os dados
		TituloCPARN tituloCPARN = new TituloCPARN();		
		TituloCPA existe = tituloCPARN.buscarPorTituloKey(this.tituloCPA.getEmpresa(),msgNumTitCPA,	msgTipoTitulo, this.tituloCPA.getFornecedor());
		
		if(existe != null){
			BigDecimal msgVlrTituloRegistrado = existe.getValorOriginal();
			FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN,"O título já está existe para esse fornecedor! No valor R$" + msgVlrTituloRegistrado , null));
			return ;
		} else {
			
				// Registra movimento na tabela E501MCP (Tabela de movimento do contas a pagar
				TituloCPAMovtoRN tituloCPAMovtRN = new TituloCPAMovtoRN();
				
				this.tituloCPAMovto.setNumTitulo(this.tituloCPA.getNumTitulo());
				this.tituloCPAMovto.setTipoTitulo(this.tituloCPA.getTipoTitulo());
				this.tituloCPAMovto.setCodigoFornecedor(this.tituloCPA.getFornecedor().getCodigoFornecedor());
				this.tituloCPAMovto.setSeqMovimento(1);
				this.tituloCPAMovto.setTnsMovimento("90500");
				this.tituloCPAMovto.setDataMovimento(this.tituloCPA.getDataEntrada());
				this.tituloCPAMovto.setValorMovimento(this.tituloCPA.getValorOriginal());
				this.tituloCPAMovto.setEmpresa(this.contextoBean.getUsuarioLogado().getEmpresa());
				this.tituloCPAMovto.setUsuario(this.contextoBean.getUsuarioLogado());
				this.tituloCPAMovto.setPlanoFinanceiro(this.tituloCPA.getPlanoFinanceiro());
				this.tituloCPAMovto.setCentroCusto(this.tituloCPA.getCentroCusto());
				
				tituloCPAMovtRN.salvar(this.tituloCPAMovto);
				
			tituloCPARN.salvar(this.tituloCPA);
			
			//mensagem cadastro realizado
			FacesMessage facesMessage = new FacesMessage("O título numero " + msgNumTitCPA + " , do Fornecedor: " + msgForTitCPA + " \n no valor R$ " + msgVlrTitCPA + " \n, foi salvo com sucesso!" );
			/*" \nFornecedor: " + msgForTitCPA +*/
			context.addMessage(null, facesMessage);
			
			
		}
		/*try{*/
		//limpa form
		this.tituloCPA = new TituloCPA();
		return;

	}