JPA - Retornando um id gerado automaticamente após persist ()

113

Estou usando JPA (EclipseLink) e Spring. Digamos que eu tenha uma entidade simples com um ID gerado automaticamente:

@Entity
public class ABC implements Serializable {
     @Id
     @GeneratedValue(strategy=GenerationType.IDENTITY)
     private int id;

     // ...
}

Na minha classe DAO, tenho um método de inserção que chama persist()essa entidade. Eu quero que o método retorne o ID gerado para a nova entidade, mas quando eu testo, ele retorna em 0vez disso.

public class ABCDao {
    @PersistenceContext
    EntityManager em;

    @Transactional(readOnly=false)
    public int insertABC(ABC abc) {
         em.persist(abc);
         // I WANT TO RETURN THE AUTO-GENERATED ID OF abc
         // HOW CAN I DO IT?
         return abc.id; // ???
    }
}

Também tenho uma classe de serviço que envolve o DAO, se isso fizer diferença:

public class ABCService {
    @Resource(name="ABCDao")
    ABCDao abcDao;

    public int addNewABC(ABC abc) {
         return abcDao.insertABC(abc);
    }
}
sura2k
fonte
Semelhante, pode consultar stackoverflow.com/q/3328813/366964
Nayan Wadekar
Obrigado pelas respostas. E como uma solução complicada (não um JPA), podemos usar outro id único, como o carimbo de data / hora unix.
sura2k

Respostas:

184

O ID só tem garantia de ser gerado no momento do flush. Persistir uma entidade apenas a torna "anexada" ao contexto de persistência. Portanto, libere o gerenciador de entidade explicitamente:

em.persist(abc);
em.flush();
return abc.getId();

ou retornar a própria entidade em vez de seu ID. Quando a transação terminar, a liberação acontecerá e os usuários da entidade fora da transação verão o ID gerado na entidade.

@Override
public ABC addNewABC(ABC abc) {
    abcDao.insertABC(abc);
    return abc;
}
JB Nizet
fonte
10
NB: isso precisa anotar o campo de id com @GeneratedValue- o que quer que isso acarrete
Mr_and_Mrs_D
Você pode explicar os problemas em tentar conseguir isso com a id composta stackoverflow.com/questions/31362100/…
bl3e
Existe uma penalidade de desempenho (ou quaisquer outros efeitos negativos) de limpar manualmente após persistir?
Craig Otis
3
Sim, há: viagem de ida e volta desnecessária ao banco de dados se a transação acabar sendo revertida, exceções em potencial se a entidade persistente (ou outras entidades liberadas) ainda não estiver em um estado válido. Um gerador de sequência ou uuid é mais simples e eficiente e não tem esses problemas porque o ID é gerado e atribuído antes que a entidade seja gravada no banco de dados.
JB Nizet
1
@JBNizet, você precisa retornar a instância ou a referência passada ainda é válida? Quer dizer, insertABCcria um novo objeto? Ou modificar o antigo?
ryvantage
13
@Entity
public class ABC implements Serializable {
     @Id
     @GeneratedValue(strategy=GenerationType.IDENTITY)
     private int id;   
}

verifique se a notação @GeneratedValue está presente em sua classe de entidade. Isso informa à JPA sobre o comportamento gerado automaticamente de sua propriedade de entidade

utkal patel
fonte
4

Foi assim que eu fiz:

EntityManager entityManager = getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
entityManager.persist(object);
transaction.commit();
long id = object.getId();
entityManager.close();
Koray Tugay
fonte
Não está funcionando dando zero como um retorno após persistir os dados na tabela. qualquer atualização não está funcionando neste caso .. O que devo fazer para isso. por favor, sugira uma maneira ... Obrigado
Vikrant Kashyap
1
@VikrantKashyap Por favor poste uma nova pergunta com pequeno código e mencione-me para que eu possa dar uma olhada.
Koray Tugay
2

Você também pode usar GenerationType.TABLE em vez de IDENTITY, que só está disponível após a inserção.

James
fonte
2
Apenas uma palavra de cautela. Quando tentei GenerationType.TABLE, ele criou uma tabela separada chamada hibernate_sequences e reiniciou a sequência de 1.
SriSri
0

Outra opção compatível com 4.0:

Antes de confirmar as alterações, você pode recuperar o CayenneDataObject(s) novo (s) objeto (s) da coleção associada ao contexto, assim:

CayenneDataObject dataObjectsCollection = (CayenneDataObject)cayenneContext.newObjects();

em seguida, acesse o ObjectIdpara cada um na coleção, como:

ObjectId objectId = dataObject.getObjectId();

Finalmente você pode iterar sob os valores, onde normalmente o ID gerado será o primeiro dos valores (para uma única chave de coluna) no Mapa retornado por getIdSnapshot(), ele contém também o (s) nome (s) da coluna associada ao PK como chave (s):

objectId.getIdSnapshot().values()
emecas
fonte
0

É assim que eu fiz. Podes tentar

    public class ABCService {
    @Resource(name="ABCDao")
    ABCDao abcDao;

    public int addNewABC(ABC abc) {
         ABC.setId(0);
         return abcDao.insertABC(abc);
    }
}
Anh Khoa
fonte
-3
em.persist(abc);
em.refresh(abc);
return abc;
Andrey
fonte
Este método não funcionou para mim. Recebi este erro: javax.persistence.PersistenceException: org.hibernate.HibernateException: esta instância ainda não existe como uma linha no banco de dados]
rtcarlson
@rtcarlson, sim, isso não vai funcionar. se você estiver criando um novo objeto, o que você precisa em.flush()não é em.refresh(abc).
Ibrahim Dauda