Dúvida com Insert em JPA

Galera, estou desenvolvendo uma aplicação em Java Swing utilizando o JPA para acesso ao PostgreSQL 9.2 e IDE Netbeans 7.3.
Minha dúvida é a seguinte:

  • Como posso inserir dois objetos ao mesmo tempo no banco de dados?
    Explico:
  • Tenho na aplicação duas classes de entidades mapeadas pelo JPA como OneToMany, quando eu realizo a inserção apenas da classe Pai o JPA insere normalmente no banco de dados, porém, quando adiciono ao meu objeto pai a lista de objetos Filhos o JPA me retorna o erro NO TRANSACTION IS CURRENTLY ACTIVE e não realiza a inserção em nenhuma das tabelas no banco. OBS: Estou fazendo isso no mesmo comando de insert, alimentando o objeto pai com a lista de objetos filhos. Desta forma:
...
objFilho.setPai(objPai);
...
listFilhos.add(objFilho);
...
objPai.setListFilhos(listFilhos);
...

Como posso resolver isso?

Desde já agradeço a ajuda de todos.

PS: Já li to tutorial de JPA do uaihebert mas não consegui resolver meu problema.

O problema é que é que você não está definindo um tipo de cascata para sua relação. Ex:


@Entity
public class Usuario {

   @id
   @GeneratedValue
   private long idUsuario;

   ...

   @OneToMany(mappedBy="usuario", cascade = CascadeType.PERSIST)
   private List<Contato> contatos;

}

@Entity
public class Carro{

   @JoinColumn(name="idUsuario")
   private Usuario usuario;

}

Fazendo assim quando vc persistir o objeto Usuario a lista de Contatos tambem será persistida.

Da uma olhada nesses links que podem te ajudar

http://docs.oracle.com/javaee/5/api/javax/persistence/OneToMany.html#cascade()
http://www.slideshare.net/jornaljava/jpa-6067119

http://www.guj.com.br/java/275094-jpa-relacionamentos-onetomay-manytoone

Espero ter ajudado.

Isso não me ajudou, continua a mesma msg de erro. Não sei o que estou fazendo de errado.

Segue parte do meu código:

@Entity
...
public class ProcedimentosGrupo implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "nr_grupo")
    private Short nrGrupo;
    ...
    @OneToMany(cascade = CascadeType.PERSIST, mappedBy = "nrGrupo")
    private List<ProcedimentosSubgrupo> procedimentosSubgrupoList;
    ...
    /**
     * Construtor da Classe.
     */
    public ProcedimentosGrupo() {
    }
 ...
}
@Entity
...
public class ProcedimentosSubgrupo implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "nr_subgrupo")
    private Short nrSubgrupo;
    ...
    @ManyToOne
    @JoinColumn(name = "nr_grupo", referencedColumnName = "nr_grupo")
    private ProcedimentosGrupo nrGrupo;
    ...
    /**
     * Construtor da Classe.
     */
    public ProcedimentosSubgrupo() {
    }
...
}

Eu estava analisando a estrutura das minhas tabelas no banco de dados, e descobri que, esta entidade está mapeada de forma incorreta, porém, existem alguns problemas, no banco de dados possuo as tabelas porte_grupo, porte_subgrupo, porte, porte_valores, sendo que, o relacionamento da tabela porte_grupo para porte_subgrupo é ManyToMany, resultando na tabela porte, porém, esta tabela também possui uma chave primária id, além das chaves estrangeiras (idGrupo, idSubgrupo) que coloquei como chaves únicas (UNIQUE), e pra piorar a situação, a tabela porte possui um relacionamento OneToMany com a tabela porte_valores, então, fica a pergunta:
Como faço para corrigir minhas classes de forma que funcione corretamente os relacionamentos do sistema? Precisarei alterar no banco os relacionamentos?

Mais uma vez agradeço a ajuda.

No meu sistema tenho as seguintes classes:
Porte:

@Entity
@Table(name = "porte", catalog = "dbsghu", schema = "dbsghu")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Porte.findAll", query = "SELECT p FROM Porte p"),
    @NamedQuery(name = "Porte.findByNrPorte", query = "SELECT p FROM Porte p WHERE p.nrPorte = :nrPorte")})
public class Porte implements Serializable {

    private static final long serialVersionUID = 1L;
    public static final String TABLE_NAME = "porte";
    /**
     * ID do Porte. Numeração gerada automaticamente pelo banco de dados.
     * <br>Coluna: &lt;tt&gt;nr_porte&lt;/tt&gt;.
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "nr_porte")
    private Short nrPorte;
    /**
     * Opção utilizada pelo sistema para informar ao usuário que o registro já
     * está sendo utilizado. <br>Coluna: &lt;tt&gt;bl_ocupado&lt;/tt&gt;.
     */
    @Column(name = "bl_ocupado")
    private boolean blOcupado;
    /**
     * Grupo do Porte. Relacionamento com a classe
     * &lt;tt&gt;entidades.PorteGrupo&lt;/tt&gt;.
     */
    @ManyToOne(optional = false)
    @JoinColumn(name = "nr_grpporte", referencedColumnName = "nr_grupoporte", unique = true)
    private PorteGrupo nrGrpporte;
    /**
     * Subgrupo do Porte. Relacionamento com a classe
     * &lt;tt&gt;entidades.PorteSubgrupo&lt;/tt&gt;.
     */
    @ManyToOne(optional = false)
    @JoinColumn(name = "nr_subgrpporte", referencedColumnName = "nr_subgrpporte", unique = true)
    private PorteSubgrupo nrSubgrpporte;
    /**
     * Lista de Valores do Porte. Relacionamento com a classe
     * &lt;tt&gt;entidades.PorteValores&lt;/tt&gt;.
     */
    @OneToMany(cascade = CascadeType.PERSIST, mappedBy = "porte", fetch = FetchType.EAGER)
    @NotFound(action = NotFoundAction.IGNORE)
    private List&lt;PorteValores&gt; porteValoresList = null;
    /**
     * Porte Anestésico. Lista de Portes Anestésicos que estão vinculados a este
     * Porte. Relacionamento com a classe
     * &lt;tt&gt;entidades.PorteAnestesico&lt;/tt&gt;.
     */
    @OneToMany(mappedBy = "nrPorte", fetch = FetchType.LAZY, orphanRemoval = true)
    @NotFound(action = NotFoundAction.IGNORE)
    private List&lt;PorteAnestesico&gt; porteAnestesicoList = null;
    /**
     * Valores de Procedimentos. Lista dos Valores dos Procedimentos que possuem
     * este Porte. Relacionamento com a classe &lt;tt&gt;ProcedimentoValores&lt;/tt&gt;.
     */
    @OneToMany(mappedBy = "nrPorte", fetch = FetchType.LAZY, orphanRemoval = true)
    @NotFound(action = NotFoundAction.IGNORE)
    private List&lt;ProcedimentoValores&gt; procedimentoValoresList = null;

    /**
     * Construtor da Classe.
     */
    public Porte() {
    }
    ...
}

PorteValores:

@Entity
@Table(name = "porte_valores", catalog = "dbsghu", schema = "dbsghu")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "PorteValores.findAll", query = "SELECT p FROM PorteValores p"),
    @NamedQuery(name = "PorteValores.findByNrPorte", query = "SELECT p FROM PorteValores p WHERE p.porteValoresPK.nrPorte = :nrPorte"),
    @NamedQuery(name = "PorteValores.findByDtVigencia", query = "SELECT p FROM PorteValores p WHERE p.porteValoresPK.dtVigencia = :dtVigencia")})
public class PorteValores implements Serializable {

    private static final long serialVersionUID = 1L;
    public static final String TABLE_NAME = "porte_valores";
    /**
     * Objeto de relacionamento com a classe Porte.
     */
    @EmbeddedId
    protected PorteValoresPK porteValoresPK;
    /**
     * Valor do Porte. <p>Coluna: &lt;tt&gt;vl_porte&lt;/tt&gt;
     */
    // @Max(value=?)  @Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation
    @Basic(optional = false)
    @Column(name = "vl_porte")
    private BigDecimal vlPorte;
    /**
     * ID do Porte. Campo utilizado pelo banco de dados para realizar o
     * relacionamento com seus Valores. <p>Coluna: &lt;tt&gt;nr_prestador&lt;/tt&gt;
     */
    @ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER, optional = false)
    @JoinColumn(name = "nr_porte", referencedColumnName = "nr_porte", insertable = false, updatable = false, nullable = false)
    private Porte porte;

    /**
     * Construtor da Classe.
     */
    public PorteValores() {
    }
    ...
}

PorteValoresPK:

@Embeddable
public class PorteValoresPK implements Serializable {

    /**
     * ID do Porte. Numeração gerada automaticamente pelo banco de dados.
     * <br>Coluna: &lt;tt&gt;nr_porte&lt;/tt&gt;
     */
    @Basic(optional = false)
    @Column(name = &quot;nr_porte&quot;)
    private short nrPorte;
    /**
     * Data de Vigência do Porte.
     */
    @Basic(optional = false)
    @Column(name = &quot;dt_vigencia&quot;)
    @Temporal(TemporalType.DATE)
    private Date dtVigencia;

    /**
     * Construtor da Classe.
     */
    public PorteValoresPK() {
    }
    ...
}

Pois bem, quando tento realizar um insert em cascata o log4j me retorna o seguinte erro.

Internal Exception: org.postgresql.util.PSQLException: ERROR: insert or update on table &quot;porte_valores&quot; violates foreign key constraint &quot;fk_portevalores1&quot;
  Detalhe: Key (nr_porte)=(0) is not present in table &quot;porte&quot;.
Error Code: 0
Call: INSERT INTO dbsghu.dbsghu.porte_valores (vl_porte, dt_vigencia, nr_porte) VALUES (?, ?, ?)
	bind =&gt; [3 parameters bound]
Query: InsertObjectQuery(entidades.PorteValores[ porteValoresPK=entidades.PorteValoresPK[ nrPorte=0, dtVigencia=2003-08-01 ] ])

Então como faço o insert em cascata de forma que o JPA alimente automaticamente o campo nrPorte da classe PorteValores?
Sei que o erro refere-se exatamente a isso, mas se é um insert em cascata por que o JPA não está passando o valor automaticamente?
É algum erro nas minhas classes?

Desde já agradeço a ajuda.

Quando você faz…

// ... public class Porte implements Serializable { // ... @OneToMany(mappedBy = "nrPorte", fetch = FetchType.LAZY, orphanRemoval = true) // note: mappedBy = "nrPorte" @NotFound(action = NotFoundAction.IGNORE) private List&lt;ProcedimentoValores&gt; procedimentoValoresList = null; // ... }
… o atributo do tipo Porte na entidade ProcedimentoValores tem que ter o mesmo nome que coloca na anotação @OneToMany(mappedBy = “nrPorte”, …. Isso não foi feito.

// ... public class PorteValores implements Serializable { // ... @ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER, optional = false) @JoinColumn(name = "nr_porte", referencedColumnName = "nr_porte", insertable = false, updatable = false, nullable = false) private Porte porte; // note: porte // ... }
Tente mudar o atributo mappedBy na anotação @OneToMany da entidade Porte para mappedBy = “porte”.

0 - Estude o JPA 2, não tente apenas ir lá, mapear de qualquer jeito e persistir. Isso é um erro muito grosseiro.
1 - Estude o mecanismo de persistência de forma gradual. Quando conseguir persistir uma entidade que não envolva tabelas adicionais, parta para o relacionamento 1:1, depois 1:N…
2 - Estude cada uma das anotações do JPA, saiba como elas funcionam, por que elas devem ou não ser inseridas, etc.
3 - Saiba como fazer manualmente, direto em SQL e JDBC.
4 - Tenha a certeza que precisa mesmo fazer da forma como está tentando fazer.

Me desculpem pela gafe, realmente foi falta de atenção minha, pois eu já havia feito outros mapeamentos sem relacionamentos entre tabelas, e outros com cadinalidades diferente, mas nenhum que utilizasse uma classe de PK para as chaves.
Bem, já corrigi a ‘gafe’ do nome do atributo, mas ainda assim, o erro persiste ao tentar realizar a inclusão do registro, é apenas no INSERT que o erro ocorre, pois quando já existe um registro no banco e faço um UPDATE o erro não ocorre.

Outra tentativa: PorteValor é dona do relacionamento entre ela e Porte, já que mappedBy está em Porte e isso indica à JPA que ela (a entidade Porte) não é a dona do relacionamento ToMany. Qual entidade você persiste? Porte ou PorteValor? Se for Porte, este é o problema. Neste caso, a JPA tenta persistir PorteValor antes de persistir Porte. Mais aqui:

Se este for o caso, mude o mapeamento para algo como?

[code]…
public class Porte … {

@OneToMany
private List<PorteValor> valores;

}


public class PorteValor … {

@ManyToOne(mappedBy=“valores”)
private Porte porte;

}[/code]

Seguinte, a entidade porte é a primeira que quero persistir, eu já havia tentado fazer o que você sugeriu (também li o tutorial do hebert) mas a anotação @ManyToOne não possui a propriedade mappedBy.
Eu, particularmente, acredito que está faltando alguma configuração com relação a classe de chaves primárias (PorteValoresPK) pois a mensagem de erro refere-se ao campo nr_porte que está sendo passado com 0, e este campo é gerado automaticamente pelo banco de dados na entidade porte, mas não encontrei nada na web com relação a isso.

Tem razão. Vacilei. Li sobre o @ManyToMany e falei besteira.