Mapear relacionamento 'aninhado'

Olá, este é apenas um exemplo pra expressar minha dúvida, se alguem ja passou por isso e puder me ajudar, agradeco.

public class Carro {

...
@Column("id_carro")
private String idCarro;

@Column(id_carro_seguro)
@Column(nome_seguro)
	
//Quero trazer o objeto "CarroSeguro" que tenha o "nome_seguro" da classe
//"Seguro" igual ao "nome_seguro" que esta nesta classe "Carro".
private CarroSeguro carroSeguro;

}

public class CarroSeguro {

...
@Column(id_carro_seguro)
private String idCarroSeguro;
	
@JoinColumn(name="id_seguro", referencedColumnName = "id_seguro")		
private Seguro s;

}

public class Seguro {

...
@Column("id_seguro")
private String idSeguro;

@Column("nome_seguro")
private String nomeSeguro;

}

Olá,

Existem várias maneiras de relacionar entidades. Para isso, você tem que saber a cardinalidade do relacionamento. Vou assumir que você quer que um carro possa ter vários seguros, e um seguro possa estar associado a vários carros, caracterizando um “n para n”, ou “muitos para muitos”.

Caso você não queira guardar nenhuma tributo extra na join table (a tabela intermediária, CarroSeguro nesse caso), você pode utilizar o mapeamento @ManyToMany. O JPA Provider (Hibernate, Eclipselink, etc) vai criar a join table automaticamente para você no banco de dados (caso ele esteja gerando as tabelas).

Funciona mais ou menos assim:

class Carro {
    //...
    @ManyToMany
    private Set<Seguro> seguros;
    //...
}

class Seguro {
    //...
    @ManyToMany(mappedBy = "seguros")
    private Set<Carro> carros;
    //...
}

Dessa forma, o JPA vai criar uma tabela no banco chamada de algo como carro_seguro, com as chaves primárias dessas duas entidades, associando-as. Repare que não é necessário criar uma classe intermediária CarroSeguro nesse caso.

Quando você associar e persistir entidades desses tipos, o jpa vai popular a join table para você. Tipo assim:

//...
Carro carro = entityManager.find(Carro.class, 1L); // busca carro com id 1
Seguro seguro = entityManager.find(Seguroc.class, 50L); // busca seguro com id 50
carro.addSeguro(seguro); // associa o seguro ao carro
//...

Dessa forma, no DB, na join table, vai aparecer a seguinte linha:

carro_id | seguro_id
1 | 50

Da próxima vez que você buscar esse carro, assim:

Carro carro = entityManager.find(Carro.class, 1L);

e pegar os seguros desse carro:

carro.getSeguros();

Ali vai aparecer o seguro com id 50, dentro da coleção.

O mesmo vale para a entidade Seguro, que vai trazer os carros associados dentro de seu conjunto carros.

Também existem outras anotações, como @ManyToOne, @OneToMany e @OneToOne, para fazer o mapeamento das outras cardinalidades e funcionam de maneira parecida, com a exceção de que não é necessária a criação de uma join table em relacionamentos que não são @ManyToMany (mas pode ser feito, caso você queira).

Recomendo que você estude os fundamentos do JPA antes de tentar utiliza-lo, porque os conceitos precisam estar bem claros na tua cabeça na hora de implementar, seja para fazer certo, seja para saber resolver problemas no caso de dar algo errado. Utilizar de forma equivocada pode te trazer mais dor de cabeça do que facilidade.

Se você souber inglês, existe um livro sensacional chamado Pro JPA 2, que explica detalhadamente o funcionamento da ferramenta. Não é muito difícil achar o PDF pra baixar, caso você não queira comprar. Em português eu não conheço nenhum material com essa qualidade, mas existem cursos online que podem te ajudar.

Boa sorte!

Agradeco a sua resposta, porém, eu apenas quero deixar este objeto mapeado para que qual quer pesquisa encima do objeto “Carro” possa ter esses joins como default.

Um exemplo que funcionou em unit tests porém nao funciona com oracle foi usar “@JoinFormula” e trazer os ids de “CarroSeguro”. O Problema é que o oracle nao aceita subquerys dentro de um left outer join…

É exatamente isso que o @ManyToMany faz. Inclusive, você pode escolher entre duas estratégias de join, LAZY (padrão, só é buscado no DB quando a coleção é acessada) e EAGER (para fazer o join o mais cedo possível). Para saber os nomes dos seguros, você vai até o conjunto de seguros e lê os nomes de cada um:

entityManager.find(Carro.class, idDoCarro).getSeguros().forEach(System.out::println);

Caso você queira só as strings, você pode utilizar JPQL, assim:

SELECT DISTINCT(s.nome) FROM Carro c JOIN FETCH c.seguros s [WHERE c.id = :idDoCarro]

Filtrando pelo id se for necessário.

@ivbarbosa tudo em ordem?? até agora aquele bug não resolvemos.

Cara, ainda não achei solução para você. To lendo de novo aquele livro que citei ali em cima, vou te responder lá no tópico.

Ok,

Ainda nao estou compreendendo… A idéia é que o objeto “CarroSeguro” seja @ManyToOne dentro do objeto “Carro”. Quando feito qual quer pesquisa usando as variaveis da entidade “Carro”, hibernate deve montar a query mais ou menos como a de baixo:
Select * from Carro c
left outer join Carro_Seguro cs
inner join Seguro s on cs.id_seguro = s.id_seguro
on cs.id_carro_seguro = c.id_carro_seguro and s.nome_seguro = c.nome_seguro
where c.cor_carro = “Azul”

Então, basta anotar o atributo seguros em Carro como @ManyToOne ao invés de @ManyToMany, e fazer com que ao invés de um Set, ele seja só uma entidade, tipo private Seguro seguro. Dessa forma, quando você fizer algo como

entityManager.createQuery(SELECT c FROM Carro c WHERE [parametros da busca])//...

O JPA vai emitir uma query para o banco de dados que faz o JOIN automático para ti, buscando o Seguro associado àquele carro. O Join acontece sozinho por causa da anotação @ManyToOne, o JPA sabe que deve fazer o Join.

Dessa forma, quando o objeto Carro é recebido, você pode fazer carro.getSeguro().getNome().

Pelo que entendi, da maneira que voce falo, eu nao precisaria da classe “Carro_Seguro”, correto?

Se for este o caso, vou pensar em completar um exemplo melhor, pois a entidade que estaria neste meio se faz necessária, uma vez que tem varios atributos a mais dentro da classe e que nao pode estar em outra.

Este exemplo é apenas ilustrativo para representar o meu problrema…

Exatamente. Se você precisa de uma tabela intermediária, a resposta é totalmente diferente.

gente eu fiz essas mudanças porém no meu servlet está dando erro.

String cod_aluno = request.getParameter("nomealuno");
Agenda agenda = new Agenda();
agenda.setAluno(cod_aluno); // o erro está aqui

esse erro apareceu depois que fiz um join no meu modal.

@ManyToOne
@JoinColumn(name="aluno")
private Aluno aluno;

alguém sabe como faço pra resolver?

Qual erro ele retorna?

eu não consigo passar cod_aluno como parametro. isso ocorreu depois que fiz esse join na model Agenda como mostra nesse código
@ManyToOne
@JoinColumn(name=“aluno”)
private Aluno aluno;

cod_aluno é uma string que está recebendo o nome do aluno. Já começa esquisito ai.
Depois voce quer setar o objeto aluno que é do tipo Aluno com esse cod_aluno do tipo string?
Nao sei o objetivo desse seu codigo, mas segue um exemplo de como seria criar um objeto e setar ele:

Aluno aluno = new Aluno();
aluno.setNome(nomeAluno);
aluno.setEtc...

agenda.setAluno(aluno);

WARN: GenerationTarget encountered exception accepting command : Error executing DDL via JDBC Statement
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL via JDBC Statement
at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:67)
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applySqlString(AbstractSchemaMigrator.java:559)
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applySqlStrings(AbstractSchemaMigrator.java:504)
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applyForeignKeys(AbstractSchemaMigrator.java:433)
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.performMigration(AbstractSchemaMigrator.java:249)
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:114)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:183)
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:72)
at org.hibernate.internal.SessionFactoryImpl.(SessionFactoryImpl.java:313)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:452)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:889)
at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:58)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:55)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:39)
at com.cleiton.Logic.DaoAgenda.salvar(DaoAgenda.java:32)
at com.cleiton.Servlet.ServletCadastroAgenda.doPost(ServletCadastroAgenda.java:49)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:650)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:218)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:110)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:506)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:962)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:445)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1115)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:637)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
Caused by: java.sql.SQLException: Cannot add foreign key constraint
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:964)

O objetivo é conseguir salvar o código do aluno no banco…
se eu tirar o join da model funciona. porém se eu tirar não vou conseguir listar o nome do aluno no outro método onde é necessário lista-lo. são tabelas que possuem relacionamentos.

ex.

	String cod_aluno = request.getParameter("nomealuno");
	String cod_professor = request.getParameter("nomeprof");
	String horario = request.getParameter("horario");
	String diasemana = request.getParameter("diasemana");
	String curso = request.getParameter("curso");
	String turno = request.getParameter("turno");
	String sala = request.getParameter("sala");
	Agenda agenda = new Agenda();
	
	Aluno aluno = new Aluno();
	
	agenda.setCodigo(cod_aluno);
	agenda.setProfessor(cod_professor);
	agenda.setHorario(horario);
	agenda.setDia(diasemana);
	agenda.setCurso(curso);
	agenda.setTurno(turno);
	agenda.setSala(sala);
	


	salvaragenda.salvar(agenda);
	RequestDispatcher r = request.getRequestDispatcher( "menu.jsp" );
	r.forward( request, response );
	
}

assim eu consigo salvar normalmente o código. porém só funciona se eu tirar o join lá do model.

o módel está dessa forma

package com.cleiton.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;

import javax.persistence.ManyToOne;

@Entity(name=“CADASTRO_AGENDA”)
public class Agenda {
@Id
//define que o código deve ser gerado automaticamente como foi definido no banco
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int codigo;
@ManyToOne
@JoinColumn(name=“aluno”)
private Aluno aluno;

private String professor;
private String horario;
private String dia;
private String aula;
private String curso;
private String turno;
private String sala;
public int getCodigo() {
	return codigo;
}
public void setCodigo(int codigo) {
	this.codigo = codigo;
}

public String getProfessor() {
	return professor;
}
public void setProfessor(String professor) {
	this.professor = professor;
}
public String getHorario() {
	return horario;
}
public void setHorario(String horario) {
	this.horario = horario;
}
public String getDia() {
	return dia;
}
public void setDia(String dia) {
	this.dia = dia;
}
public String getAula() {
	return aula;
}
public void setAula(String aula) {
	this.aula = aula;
}
public String getCurso() {
	return curso;
}
public void setCurso(String curso) {
	this.curso = curso;
}
public String getTurno() {
	return turno;
}
public void setTurno(String turno) {
	this.turno = turno;
}
public String getSala() {
	return sala;
}
public void setSala(String sala) {
	this.sala = sala;
}
public Aluno getAluno() {
	return aluno;
}
public void setAluno(Aluno aluno) {
	this.aluno = aluno;
}

}

 <%
    	DaoAgenda resultagenda = new DaoAgenda();
         
               
            	List<Agenda> lista_agenda = resultagenda.listaTodos();
            	
            	for(Agenda p: lista_agenda){
    %>
         <td width=1000><%= p.getAluno().getNome() %></td>  //O JOIN QUE EU FIZ NO MODEL AGENDA FOI PRA UTILIZADA PARA CONSEGUIR LISTAR O NOME DO ALUNO EM VEZ DO CÓDIGO.
         <td width=1000><%= p.getProfessor() %></td>
         <td width=1000><%= p.getCurso() %></td>
         <td width=1000><%= p.getDia() %></td>
          <td width=1000><%= p.getTurno() %></td>
            <td width=1000><%= p.getSala() %></td>
         </tr>  
  
    </tbody>
     <% } %>

O código do aluno é do objeto aluno e não do atributo de agenda.

Caused by: java.sql.SQLException: Cannot add foreign key constraint

String cod_aluno = request.getParameter("nomealuno"); //está recuperando nome e não codigo que deve ser atributo do tipo int
String cod_professor = request.getParameter("nomeprof");/*recuperando nome e não cod. que devem ser uma chave do tipo int*/

Como está a entidade Aluno?
Nela você precisa colocar a anotação

@OneToMany(mappedBy = “aluno”) /indica que a agenda será dona da relação ou seja, ela terá o ID que referencia o Aluno na tabela/
private Agenda agenda;

Sei que é complicado e você pode está com tempo corrido de prazo.
Mas para se fazer projetos que utilizem FrameWork deve haver estudo antes, pois se não será um B.O atrás do outro e tudo vai parecer muito complicado. Go Go, recomendo os livros da casa do código, sobre JPA
e a apostila aberta da Caelum. Se tivesse escolhido combinar com Spring Seria mais rápido ainda. Mas novamente, é necessario ter uma base boa para brincar com eles e tente evitar postagem em diversos lugares, crie apenas um post único e aguarde resposta somente nele.