JPA — операции INSERT, UPDATE, DELETE. Примеры запросов вставки, удаления, обновления на JPQL.
Операции вставки, удаления и обновления данных в базе данных с использованием JPA выполняются достаточно просто.
Используемые технологии
- Spring 4.1.5.RELEASE
- Hibernate 5.0.1.Final
- JPA 2.1
- MySQL 5.6.25
- IntelliJ IDEA 14
- Maven 3.2.5
1. Структура проекта
Весь код и таблицы из JPA – пример приложения Hello World.
2. Изменения в классах сущностей для возможности операций вставки, удаления и обновления.
Сущностные классы из указанного выше проекта подверглись изменениям. Класс ContactEntity:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
package ru.javastudy.entities; import javax.persistence.*; import java.util.Date; import java.util.HashSet; import java.util.Set; @Entity @Table(name = "contact", schema = "", catalog = "javastudy") @NamedQueries({ @NamedQuery(name = "ContactEntity.findAll", query = "select c from ContactEntity c"), @NamedQuery(name = "ContactEntity.findById", query = "select distinct c from ContactEntity c left join fetch c.contactTelDetails t left join fetch c.hobbies h where c.id = :id"), @NamedQuery(name="ContactEntity.findAllWithDetail", query="select distinct c from ContactEntity c left join fetch c.contactTelDetails t left join fetch c.hobbies h") }) public class ContactEntity { private Integer id; private String firstName; private String lastName; private Date birthDate; //java.util.Date. Not java.sql.Date. @see @Temporal(TemporalType.DATE) private int version; public ContactEntity() { } public ContactEntity(String firstName, String lastName, Date birthDate) { this.firstName = firstName; this.lastName = lastName; this.birthDate = birthDate; } @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false, insertable = true, updatable = true) public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Basic @Column(name = "first_name", nullable = false, insertable = true, updatable = true, length = 60) public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } @Basic @Column(name = "last_name", nullable = false, insertable = true, updatable = true, length = 40) public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @Basic @Temporal(TemporalType.DATE) //can use java.util.Date instead sql.Date @Column(name = "birth_date", nullable = true, insertable = true, updatable = true) public Date getBirthDate() { return birthDate; } public void setBirthDate(Date birthDate) { this.birthDate = birthDate; } @Basic @Column(name = "version", nullable = false, insertable = true, updatable = true) public int getVersion() { return version; } public void setVersion(int version) { this.version = version; } private Set<ContactTelDetailEntity> contactTelDetails = new HashSet<ContactTelDetailEntity>(); /* mappedBy - свойство в ContactTelDetailEntity, связанное с внешнем ключом в этой таблице cascade - операция обновления должна распространяться на дочерние записи orphanRemoval - после обновления, записи которых больше нет в наборе должны быть удалены из БД */ @OneToMany(mappedBy = "contact", cascade = CascadeType.ALL, orphanRemoval = true) public Set<ContactTelDetailEntity> getContactTelDetails() { return this.contactTelDetails; } public void setContactTelDetails(Set<ContactTelDetailEntity> contactTelDetails) { this.contactTelDetails = contactTelDetails; } public void addContactTelDetail(ContactTelDetailEntity contactTelDetail) { contactTelDetail.setContact(this); getContactTelDetails().add(contactTelDetail); } public void removeContactTelDetail(ContactTelDetailEntity contactTelDetai) { getContactTelDetails().remove(contactTelDetai); } private Set<HobbyEntity> hobbies = new HashSet<HobbyEntity>(); @ManyToMany @JoinTable(name = "contact_hobby_detail", //foreign key for ContactEntity in contact_hobby_detail table joinColumns = @JoinColumn(name = "contact_id"), //key for other side - hobby table inverseJoinColumns = @JoinColumn(name = "hobby_id")) public Set<HobbyEntity> getHobbies() { return hobbies; } public void setHobbies(Set<HobbyEntity> hobbies) { this.hobbies = hobbies; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ContactEntity that = (ContactEntity) o; if (id != null ? !id.equals(that.id) : that.id != null) return false; if (firstName != null ? !firstName.equals(that.firstName) : that.firstName != null) return false; if (lastName != null ? !lastName.equals(that.lastName) : that.lastName != null) return false; return !(birthDate != null ? !birthDate.equals(that.birthDate) : that.birthDate != null); } @Override public int hashCode() { int result = id != null ? id.hashCode() : 0; result = 31 * result + (firstName != null ? firstName.hashCode() : 0); result = 31 * result + (lastName != null ? lastName.hashCode() : 0); result = 31 * result + (birthDate != null ? birthDate.hashCode() : 0); return result; } @Override public String toString() { return "ContactEntity{" + "id=" + id + ", " + firstName + '\'' + ", " + lastName + '\'' + ", " + birthDate + ", " + version + '}'; } } |
Добавлена аннотация @GeneratedValue(strategy = GenerationType.IDENTITY), которая указывает на стратегию создания id при вставке новых данных.
Добавлена аннотация @Temporal(TemporalType.DATE), которая позволяет использовать java.util.Date вместо sql.Date.
Класс ContactTelDetailEntity изменился аналогичным образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
package ru.javastudy.entities; import javax.persistence.*; @Entity @Table(name = "contact_tel_detail", schema = "", catalog = "javastudy") public class ContactTelDetailEntity { private Integer id; private String telType; private String telNumber; private int version; private ContactEntity contactByContactId; public ContactTelDetailEntity() { } public ContactTelDetailEntity(String telType, String telNumber) { this.telType = telType; this.telNumber = telNumber; } @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false, insertable = true, updatable = true) public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Basic @Column(name = "tel_type", nullable = false, insertable = true, updatable = true, length = 20) public String getTelType() { return telType; } public void setTelType(String telType) { this.telType = telType; } @Basic @Column(name = "tel_number", nullable = false, insertable = true, updatable = true, length = 20) public String getTelNumber() { return telNumber; } public void setTelNumber(String telNumber) { this.telNumber = telNumber; } @Basic @Column(name = "version", nullable = false, insertable = true, updatable = true) public int getVersion() { return version; } public void setVersion(int version) { this.version = version; } private ContactEntity contact; @ManyToOne @JoinColumn(name = "contact_id") public ContactEntity getContact() { return this.contact; } public void setContact(ContactEntity contact) { this.contact = contact; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ContactTelDetailEntity that = (ContactTelDetailEntity) o; if (id != null ? !id.equals(that.id) : that.id != null) return false; if (telType != null ? !telType.equals(that.telType) : that.telType != null) return false; return !(telNumber != null ? !telNumber.equals(that.telNumber) : that.telNumber != null); } @Override public int hashCode() { int result = id != null ? id.hashCode() : 0; result = 31 * result + (telType != null ? telType.hashCode() : 0); result = 31 * result + (telNumber != null ? telNumber.hashCode() : 0); return result; } @Override public String toString() { return "ContactTelDetailEntity{" + "version=" + version + ", telNumber='" + telNumber + '\'' + ", telType='" + telType + '\'' + ", id=" + id + '}'; } } |
3. JPA — вставка данных (insert) с помощью метода persist()
Операция вставки производится очень легко. Приведу пример сервиса с методами вставки, удаления и обновления ContactService:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package ru.javastudy.intf; import ru.javastudy.entities.ContactEntity; import java.util.List; public interface ContactService { //остальной код не показан public ContactEntity save(ContactEntity contact); // Удалить контакт. public void delete(ContactEntity contact); } |
Его реализация ContactServiceImpl:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
package ru.javastudy.impl; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ru.javastudy.entities.ContactEntity; import ru.javastudy.intf.ContactService; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; import java.util.List; @Repository @Service("jpaContactService") @Transactional public class ContactServiceImpl implements ContactService { @PersistenceContext private EntityManager em; // остальной код не показан public ContactEntity save(ContactEntity contact) { if (contact.getId() == null) { em.persist(contact); } else { em.merge(contact); } System.out.println("Contact saved with id: " + contact.getId()); return contact; } public void delete(ContactEntity contact) { ContactEntity mergedContact = em.merge(contact); em.remove(mergedContact); System.out.println("Contact with id: " + mergedContact.getId() + " deleted successfully"); } } |
Чтобы вставить запись или обновить ее используем метод save(). Сначала проверяется существование id у контакта, если его нет, то выполняем операцию persist(). Когда вызывается метод persist(), диспетчер сущностей (EntityManager) сохранит сущность и сделает ее управляемым экземпляром в рамках контекста постоянства. Если значение id существует, значит, производится обновление, и тогда будет вызван метод EntityManager.merge(). При вызове метода merge() диспетчер сущностей объединит состояние сущности с текущим контекстом постоянства.
3.1 Тестирование операции вставки
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public static void main(String[] args) { GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(); ctx.load("classpath:spring-config.xml"); ctx.refresh(); insertUpdateExample(ctx); } private static void insertUpdateExample(GenericXmlApplicationContext ctx) { ContactService service = ctx.getBean("jpaContactService", ContactService.class); ContactEntity contact = new ContactEntity(); contact.addContactTelDetail(new ContactTelDetailEntity("Городской", "8-499-000-333")); contact.setFirstName("NameInsert"); contact.setLastName("LastNameInsert"); contact.setBirthDate(new java.util.Date()); ContactTelDetailEntity contactTelDetail = new ContactTelDetailEntity("Home", "1111111111"); contact.addContactTelDetail(contactTelDetail); contactTelDetail = new ContactTelDetailEntity("Городской", "8-499-000-333"); contact.addContactTelDetail(contactTelDetail); contactTelDetail = new ContactTelDetailEntity("Mobile", "2222222222"); contact.addContactTelDetail(contactTelDetail); service.save(contact); } |
Добавляем данные в сущность контакт и вызываем метод save(). Произойдет вставка данных в базу данных.
4. JPA — обновление данных (update) с помощью метода merge()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
private static void insertUpdateExample(GenericXmlApplicationContext ctx) { //остальной код на показан /*update example */ ContactEntity existContact = service.findById(contact.getId()); //contact.id - insert before existContact.setLastName("updatedLastName"); existContact.setFirstName("updatedNewName"); ContactTelDetailEntity toDeleteTel = null; for(ContactTelDetailEntity c : existContact.getContactTelDetails()) { if (c.getTelType().equals("Mobile")) { toDeleteTel = c; } } existContact.removeContactTelDetail(toDeleteTel); service.save(existContact); } |
Используем тот же insertUpdateExample() из прошлого примера и объект contact, который только что добавили в базу данных. Обновляем данные имени и фамилии, а так же записываем объект с деталями телефона. Далее его удаляем из контакта и вызываем метод save(). В этот раз будет вызван метод merge() из ContactServiceImpl.
Данные имени и фамилии будут обновлены, но так же будет удален телефон, связанный с этим контактом. Это возможно, т.к. в ассоциации один-ко-многим выставлен атрибут orphanRemoval=true, который указывает поставщику JPA (в нашем случае Hibernate) на необходимость удаления всех висячих записей, существующих в базе данных, но не принадлежащих какому-либо сохраненному объекту.
1 2 3 4 |
@OneToMany(mappedBy = "contact", cascade = CascadeType.ALL, orphanRemoval = true) public Set<ContactTelDetailEntity> getContactTelDetails() { return this.contactTelDetails; } |
5. JPA — удаление данных с помощью метода remove()
Операция удаления в JPA производится в несколько строк:
1 2 3 4 5 |
private static void deleteExample(GenericXmlApplicationContext ctx) { ContactService service = ctx.getBean("jpaContactService", ContactService.class); ContactEntity contactEntity = service.findById(28); service.delete(contactEntity); } |
Будет вызван метод EntityManager.remove() и объект с указанным id будет удален из базы данных. Будет удалена так же вся связанная информация — детали телефонных номеров и хобби, поскольку в сущности указано cascade=CascadeType.ALL.
Исходные коды
JPA Tutorial — insert, delete,update
6