JPA работа с базой данных

В данном уроке я бы хотел вам показать как работать с базой данных используя JPA API.

В уроке ‘Работа с базами данных с помощью JDBC драйвера‘ мы уже разбирали, как можно работать с БД прямыми SQL запросами, а также в ‘Как работать с MySQL в Java — Hibernate XML Mapping‘.

JPA облегчает работу программисту, так как берет на себя все заботы по работе с базой данных.

В этом уроке я  покажу, как на основе классов строить базу данных, как выполнять CRUD(Create Read Update Delete) операции не используя SQL запросов.

Шаг 1

Создаем maven проект и добавляем следующую зависимость:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>JPAHiberanateExample</groupId>
    <artifactId>JPAHiberanateExample</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.25</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>4.2.1.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.0-api</artifactId>
            <version>1.0.1.Final</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>
    </dependencies>

</project>

Вы наверное заметили, что в зависимостях используется JUnit, с его помощью мы будем тестировать наши CRUD методы.

Шаг 2

Одной из крутых возможностей JPA — это создание таблиц в базе данных на основе существующий классов сущностей.

Entity — это сущность какого-то объекта, который можно описать некоторыми атрибутами.

В качестве предметной области мы возьмем магазин по продаже автомобилей.

Первая сущность это Автомобиль:

package com.devcolibri.entity;

import java.util.Date;

public class Car {

    private String name;      //Название авто
    private Date releaseDate; //дата выпуска
    private float cost;       //стоимость

    public Car(String name, Date releaseDate, float cost) {
        this.name = name;
        this.releaseDate = releaseDate;
        this.cost = cost;
    }

    public Car() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getReleaseDate() {
        return releaseDate;
    }

    public void setReleaseDate(Date releaseDate) {
        this.releaseDate = releaseDate;
    }

    public float getCost() {
        return cost;
    }

    public void setCost(float cost) {
        this.cost = cost;
    }

    @Override
    public String toString() {
        return "Car{" +
                "name='" + name + '\'' +
                ", releaseDate=" + releaseDate +
                ", cost=" + cost +
                '}';
    }
}

В сущности Car мы переопределили метод toString(), чтобы выводя объект в консоль мы видели его в человеческом виде.

Вторая сущность — это продавец:

package com.devcolibri.entity;

public class Seller {

    private String fullName; // Полное имя
    private int age;         // Возвраст
    private float salary;    // Зар. плата

    public Seller(String fullName, int age, float salary) {
        this.fullName = fullName;
        this.age = age;
        this.salary = salary;
    }

    public Seller() {
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public float getSalary() {
        return salary;
    }

    public void setSalary(float salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Seller{" +
                "fullName='" + fullName + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

Шаг 3

Теперь давайте создадим базу данных:

CREATE SCHEMA `carshop` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

Шаг 4

Теперь нужно проаннотировать наши сущности я на примере Car класса.

package com.devcolibri.entity;

import javax.persistence.*;
import java.util.Date;

@Entity
@Table(name = "cars")
@NamedQuery(name = "Car.getAll", query = "SELECT c from Car c")
public class Car {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column(name = "name", length = 32)
    private String name;      //Название авто

    @Column(name = "date")
    @Temporal(TemporalType.TIMESTAMP)
    private Date releaseDate; //дата выпуска

    @Column(name = "cost")
    private float cost;       //стоимость

    public Car(String name, Date releaseDate, float cost) {
        this.name = name;
        this.releaseDate = releaseDate;
        this.cost = cost;
    }

    public Car() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getReleaseDate() {
        return releaseDate;
    }

    public void setReleaseDate(Date releaseDate) {
        this.releaseDate = releaseDate;
    }

    public float getCost() {
        return cost;
    }

    public void setCost(float cost) {
        this.cost = cost;
    }

    public long getId() {
        return id;
    }

    @Override
    public String toString() {
        return "Car{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", releaseDate=" + releaseDate +
                ", cost=" + cost +
                '}';
    }
}

В строке 8 вы увидите NamedNativeQuery — это запрос, который относится к сущности Car и с его помощью мы будем получать список всех машин в базе.

Шаг 4

Теперь создадим конфигурационный файл для JPA называется он ‘src\main\resources\META-INF\persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
                                 http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
             version="1.0">

    <persistence-unit name="COLIBRI" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>

        <properties>
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/carshop"/>
            <property name="hibernate.connection.username" value="root"/>
            <property name="hibernate.connection.password" value="root"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
        </properties>
    </persistence-unit>
</persistence>

Рассмотри параметры:

hibernate.connection.driver_class — здесь мы говорим какой драйвер использовать для работы с базой данных;

hibernate.connection.url — тут указываем URL к базе;

hibernate.connection.username — имя пользователя этой базы;

hibernate.connection.password — и его пароль;

hibernate.dialect — тут мы устанавливаем диалект текущей БД, он дает возможность использовать возможность генерации ключей, он автоматизирует всю эту работу;

hibernate.hbm2ddl.auto — тут статус работы JPA:

update — база будет просто обновлять свою структуру;

validate — проверяет структуру базы но не вносит изменения;

create — создает таблицы, но уничтожает предыдущие данные;

create-drop — создает таблицы в начале сеанса и удаляет их по окончанию сеанса.

Внимание!
Обратите внимание на тег persistence-unit атрибут name=»COLIBRI», на это имя мы в дальнейшем будем создавать EntityManager для работы с Entity.

Теперь посмотрим структуру проекта:

Шаг 5

Создаем класс-сервис, который будет обеспечивать работу с БД CarService.java:

package com.devcolibri.crud;

import com.devcolibri.entity.Car;

import javax.persistence.EntityManager;
import javax.persistence.Persistence;
import javax.persistence.TypedQuery;
import java.util.List;

public class CarService {

    public EntityManager em = Persistence.createEntityManagerFactory("COLIBRI").createEntityManager();

    public Car add(Car car){
        em.getTransaction().begin();
        Car carFromDB = em.merge(car);
        em.getTransaction().commit();
        return carFromDB;
    }

    public void delete(long id){
        em.getTransaction().begin();
        em.remove(get(id));
        em.getTransaction.commit();
    }

    public Car get(long id){
        return em.find(Car.class, id);
    }

    public void update(Car car){
        em.getTransaction().begin();
        em.merge(car);
        em.getTransaction().commit();
    }

    public List<Car> getAll(){
        TypedQuery<Car> namedQuery = em.createNamedQuery("Car.getAll", Car.class);
        return namedQuery.getResultList();
    }

}

В строке 36 мы вызываем NamedNativeQuery по его имени.

Если вы используете IntellijIdea, то должны увидеть, что в строке 36 подчеркивается красным Car.getAll так как ваш проект еще не знает, что вы используете JPA он не опознает эту запись.

Давайте укажем для нашего проекта, что мы используем JPA:

Заходим в File->Project Structure->Modules и в списке где ваш проект нажимаете по нему и выбираете New->JPA:

После жмем плюс и выбираем persistence.xml.

После этого у вас должно пропасть подчеркивание.

Шаг 6

Для класса CarService мы напишем следующий тестовый класс который проверит работоспособность методов:

package com.devcolibri.testing;

import com.devcolibri.crud.CarService;
import com.devcolibri.entity.Car;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class CarServiceTest {

    CarService service = new CarService();

    @Test
    public void testSaveRecord() throws Exception {
        //Создаем автомобиль для записи в БД
        Car car1 = new Car();
        car1.setName("BMW");
        car1.setCost(20000);
        car1.setReleaseDate(new Date());

        //Записали в БД
        Car car = service.add(car1);

        //Вывели записанную в БД запись
        System.out.println(car);
    }

    @Test
    public void testDeleteRecord() throws Exception {
        //Создаем автомобиль для записи в БД
        Car car1 = new Car();
        car1.setName("Ferrari");
        car1.setCost(100000);
        car1.setReleaseDate(new Date());

        //Записуем его в БД
        Car car = service.add(car1);

        //Удвлем его с БД
        service.delete(car.getId());
    }

    @Test
    public void testSelect() throws Exception {
        //Создаем автомобиль для записи в БД
        Car car1 = new Car();
        car1.setName("Citroen‎");
        car1.setCost(30000);
        car1.setReleaseDate(new Date());

        //Записываем в БД
        Car car = service.add(car1);

        //Получние с БД Citroen‎
        Car carFromDB = service.get(car.getId());
        System.out.println(carFromDB);
    }

    @Test
    public void testUpdate() throws Exception {
        //Создаем автомобиль для записи в БД
        Car car1 = new Car();
        car1.setName("Lambordshini");
        car1.setCost(5000000);
        car1.setReleaseDate(new Date());

        //Записываем в БД
        car1 = service.add(car1);

        car1.setCost(0);

        //Обновляем
        service.update(car1);

        //Получаем обновленую запись
        Car car2 = service.get(car1.getId());
        System.out.println(car2);
    }

    public void testGetAll(){
        //Создаем автомобиль для записи в БД
        Car car1 = new Car();
        car1.setName("Lexus");
        car1.setCost(300000);
        car1.setReleaseDate(new Date());

        //Создаем автомобиль для записи в БД
        Car car2 = new Car();
        car2.setName("Fiat");
        car2.setCost(20000);
        car2.setReleaseDate(new Date());

        //Создаем автомобиль для записи в БД
        Car car3 = new Car();
        car3.setName("Porsche");
        car3.setCost(458000);
        car3.setReleaseDate(new Date());

        //Сохраняем все авто
        service.add(car1);
        service.add(car2);
        service.add(car3);

        //Получаем все авто с БД
        List<Car> cars = service.getAll();

        //Выводим полученый список авто
        for(Car c : cars){
            System.out.println(c);
        }
    }

}
Урок создан: 18 мая 2013 | Просмотров: 76338 | Автор: Александр Барчук | Правила перепечатки


Добавить комментарий

Добавить комментарий

Ваш e-mail не будет опубликован.

Комментарии:

  • 19 мая 2013 в 14:48

    Андрей

    Очень познавательно. Спасибо!

  • 01 июня 2013 в 23:07

    Vikki

    Спасибо больше, отличный пример! Добавьте еще пример со связями в таблицах =)

  • 05 августа 2013 в 23:07

    Ang

    Я извиняюсь.

    Но мне кажется в шаге 4 ошибка вот в этой строке: @NamedQuery(name = «Car.getAll», query = «SELECT c from Car c»)
    Запрос не правильно написан, надо наверно дописать select c.* from car c

    Хотя я может и ошибаюсь.

    • 06 августа 2013 в 09:11

      Александр Барчук

      Нет, все правильно всегда так пишу, и пока не жалуюсь :)

      • 06 августа 2013 в 10:32

        Ang

        В MSSQL «SELECT c from Car c» уже такое не прокатывает, пока не пропишешь c.* Хотя это может быть внутренние механизмы hibernate дописывают.

        • 06 августа 2013 в 11:45

          Александр Барчук

          Скорее все это MSSQL, а в hibernate все универсально.

          • 06 марта 2015 в 23:30

            Alex

            В аттрибуте @NamedQuery запрос написан на JPA Query Language, а не SQL.

  • 22 декабря 2013 в 17:18

    Artur

    Odin k odnomu, perepisal v IDE. Nichego ne rabotaet!!! Skachal primer, sootvestviye, to shto na saite ostayet jelat’ luchshego!!!!

  • 20 января 2014 в 01:09

    Kirill

    Проверяйте чтобы пошаговые уроки на скринах были правильные, тут нифига не схоидтся!

  • 04 марта 2014 в 18:08

    Anon

    Зачем в методе add() возвращать значение em.merge(car), почему не возвращать фактический параметр, который передавался в метод?
    Какое отличие update() от add(), кроме возвращение объекта?

    • 05 марта 2014 в 10:03

      Александр Барчук

      При сохранениии объекта вам нужно знать его id, так как до этого вы его не знали, а при обновлении вы обновляете существующую запись поэтому id вы уже знаете.

  • 27 марта 2014 в 16:50

    Oleg

    Расположение файла persistence.xml в директории src\main\resources\META-INF — это стандарт? Как изменить расположение или переименовать его?

    • 27 марта 2014 в 18:18

      Александр Барчук

      Да это стандарт.. Если вы используете чистый JPA то тут ниче поменять нельзя на сколько мне известно.

  • 21 апреля 2014 в 20:17

    Илья

    А можете объяснить где прописывать Шаг 3? Как создать саму БД?

  • 03 августа 2014 в 14:18

    alex

    tomcat ругается на отсутствие Web.xml , как убрать эту ошибку, без создания Web.xml

    • 27 июня 2016 в 13:40

      Dmytro

      org.apache.maven.plugins
      maven-war-plugin

      false

  • 09 ноября 2014 в 02:54

    Макс

    Подскажи пожалуйста. Я так понимаю ORM можно реализовать используя чистый hibernate, а можно через JPA, а как предпочтительней? А то я что то запутался :(. Или если я ошибаюсь поправь пожалуйста.

    • 30 ноября 2014 в 16:21

      Илья

      Да, похоже на то что вы таки немного запутались. Насколько мне известно JPA — это только «спецификация», а Hibernate — это уже реализация этой спецификации.

  • 30 ноября 2014 в 22:56

    provisota

    Отличная статья, прикрутил по ней хибер к GWT проекту, всё завелось с «пол пинка» =)
    Спасибо!

  • 20 января 2015 в 09:52

    umka

    Деньги нельзя представлять в виде чисел с плавающей точкой!

  • 05 февраля 2015 в 13:43

    betula91

    А какова роль Seller ?

    • 16 июля 2017 в 19:24

      Виктор

      Действительно, этот класс тут лишний.

  • 06 марта 2015 в 23:34

    Alex

    «В строке 36 мы вызываем NamedNativeQuery по его имени.» — здесь ошибка. Правильно — NamedQuery.
    NamedQuery для JPQL, NamedNativeQuery — SQL.

  • 12 апреля 2015 в 13:55

    Костя

    Привет.Отличный пример!Но у меня вопрос по методу merge.Он всегда вставляет новую сущность в базу,а не апдейтит существующую.В чем может быть проблема?

  • 24 мая 2015 в 16:34

    Александр

    Подскажите исходя из данного примера, нужно ли вручную закрывать EntityManager или за нас будет это делать контейнер?

  • 17 июля 2015 в 22:23

    NightCapybara

    Для тех, кто копирует код один-в-один, в методе

    public void delete(long id){
    em.getTransaction().begin();
    em.remove(get(id));
    em.getTransaction.commit();
    }
    ошибка
    em.getTransaction() .commit(); — после гетТранзакшн скобки! (Вызов метода)

    Также, чтобы не создавать бд, измените аттрибут на — Хибернейт сам создаст БД

  • 20 июля 2015 в 23:45

    Григорий

    А у меня выкидывает java.lang.ClassNotFoundException: hibernate.Test
    правда до этого я юзал чисто хибернейт

  • 02 сентября 2015 в 16:19

    Евгения

    Большое спасибо! Помог ваш пример!

  • 23 сентября 2015 в 17:55

    Sergey

    Здравствуйте. Все сделал как описано, но в классе Car подчеркивает в строке «@Table(name = «cars»)» «cars». Ну и соответственно все имена в аннотациях @Column, также подчеркнутые. Тесты пример не проходит.

    • 23 сентября 2015 в 17:59

      Sergey

      Здравствуйте. Все сделал как описано, но в классе Car подчеркивает в строке «@Table(name = «cars»)» «cars». Ну и соответственно все имена в аннотациях @Column, также подчеркнутые. Тесты пример не проходит. IDEA сообщает ошибку: This inspection controls whether the Persistence ORM annotations are checked against configured Datasources

  • 10 декабря 2015 в 15:17

    Sergiy

    Спасибо, отличный пример!

  • 24 августа 2016 в 12:14

    Светлана

    Пример отличный! Подскажите как JPA модуль подключить, если его нет в списке выбора модулей? В плагинах IDEA Community нет Java EE. Как быть?

  • 03 октября 2016 в 14:15

    mihoj

    В каждом методе CarService добавить em.close(); по желанию можно, так сказать для работоспособности =)

  • 03 января 2017 в 18:30

    азамат

    при редактировании разве не надо указывать айди+ объект ??

  • 04 апреля 2017 в 15:07

    Leonid

    Это потрясающе! Спасибо большое!

  • 15 мая 2017 в 12:02

    Илья

    Александр, добрый день. Я новичок в Java, в целом код хорошо написан и понятен, но у меня остался вопрос относительно запроса на шаге 4 «name = «Car.getAll», query = «SELECT c from Car c», а именно:
    Почему «c»? Что такое «с» и где оно определяется?
    Если у меня есть класс, написанный кем-то другим, как мне выбрать объекты из него? Какую «букву» испльзовать там?

    Заранее благодарю.

  • 16 июля 2017 в 19:12

    Виктор

    Код рабочий. Тесты прошли сразу. Делал всё строго по инструкции.