1. Información general
En este tutorial rápido, aprenderemos cómo realizar una instrucción INSERT en objetos JPA .
Para obtener más información sobre Hibernate en general, consulte nuestra guía completa de JPA con Spring y la introducción a Spring Data con JPA para profundizar en este tema.
2. Objetos persistentes en JPA
En JPA, cada entidad que pasa de un estado transitorio a un estado gestionado es manejada automáticamente por EntityManager .
Los EntityManager comprueba si una entidad dada ya existe y después decide si se debe insertar o actualizar. Debido a esta gestión automática, t él sólo declaraciones permitidas por la APP son SELECT, UPDATE y DELETE.
En los ejemplos siguientes, veremos diferentes formas de gestionar y superar esta limitación.
3. Definición de un modelo común
Ahora, comencemos por definir una entidad simple que usaremos a lo largo de este tutorial:
@Entity public class Person { @Id private Long id; private String firstName; private String lastName; // standard getters and setters, default and all-args constructors }
Además, definamos una clase de repositorio que usaremos para nuestras implementaciones:
@Repository public class PersonInsertRepository { @PersistenceContext private EntityManager entityManager; }
Además, aplicaremos la anotación @Transactional para manejar transacciones automáticamente por Spring. De esta manera, no tendremos que preocuparnos por crear transacciones con nuestro EntityManager, confirmar nuestros cambios o realizar una reversión manualmente en el caso de una excepción.
4. createNativeQuery
Para consultas creadas manualmente, podemos usar el método EntityManager # createNativeQuery . Nos permite crear cualquier tipo de consulta SQL, no solo las soportadas por JPA. Agreguemos un nuevo método a nuestra clase de repositorio:
@Transactional public void insertWithQuery(Person person) { entityManager.createNativeQuery("INSERT INTO person (id, first_name, last_name) VALUES (?,?,?)") .setParameter(1, person.getId()) .setParameter(2, person.getFirstName()) .setParameter(3, person.getLastName()) .executeUpdate(); }
Con este enfoque, necesitamos definir una consulta literal que incluya los nombres de las columnas y establecer sus valores correspondientes.
Ahora podemos probar nuestro repositorio:
@Test public void givenPersonEntity_whenInsertedTwiceWithNativeQuery_thenPersistenceExceptionExceptionIsThrown() { Person person = new Person(1L, "firstname", "lastname"); assertThatExceptionOfType(PersistenceException.class).isThrownBy(() -> { personInsertRepository.insertWithQuery(PERSON); personInsertRepository.insertWithQuery(PERSON); }); }
En nuestra prueba, cada operación intenta insertar una nueva entrada en nuestra base de datos. Como intentamos insertar dos entidades con la misma identificación , la segunda operación de inserción falla al lanzar una PersistenceException .
El principio aquí es el mismo si usamos @Query de Spring Data .
5. persistir
En nuestro ejemplo anterior, creamos consultas de inserción, pero tuvimos que crear consultas literales para cada entidad. Este enfoque no es muy eficiente y da como resultado una gran cantidad de código repetitivo.
En cambio, podemos hacer uso de la persistir método de EntityManager .
Como en nuestro ejemplo anterior, ampliemos nuestra clase de repositorio con un método personalizado:
@Transactional public void insertWithEntityManager(Person person) { this.entityManager.persist(person); }
Ahora, podemos probar nuestro enfoque nuevamente :
@Test public void givenPersonEntity_whenInsertedTwiceWithEntityManager_thenEntityExistsExceptionIsThrown() { assertThatExceptionOfType(EntityExistsException.class).isThrownBy(() -> { personInsertRepository.insertWithEntityManager(new Person(1L, "firstname", "lastname")); personInsertRepository.insertWithEntityManager(new Person(1L, "firstname", "lastname")); }); }
A diferencia del uso de consultas nativas, no tenemos que especificar los nombres de las columnas y los valores correspondientes . En cambio, EntityManager se encarga de eso por nosotros.
En la prueba anterior, también esperamos que se lance EntityExistsException en lugar de su superclase PersistenceException, que es más especializada y la arroja persist .
Por otro lado, en este ejemplo, debemos asegurarnos de llamar a nuestro método insert cada vez con una nueva instancia de Person. De lo contrario, EntityManager ya lo administrará, lo que dará como resultado una operación de actualización.
6. Conclusión
En este artículo, ilustramos formas de realizar operaciones de inserción en objetos JPA. Observamos ejemplos del uso de una consulta nativa, así como el uso de EntityManager # persist para crear declaraciones INSERT personalizadas.
Como siempre, el código completo utilizado en este artículo está disponible en GitHub.