• ,

Продолжение разбора тестового задания

В продолжение топика про полученное мной задание публикую разбор таких вопросов как прикручивание БД к проекту Spring Boot, соединение БД к контроллеру, вывод данных из бд в браузер в формате JSON.

Прикрутим базу данных

Начну как обычно с лирики…
Если вы пробовали настраивать соединение с базой данных, не важно что это за база данных: PostgreSQL, MySQL или встраеваемые базы данных типа H2 или HSQLDB, не важно какой технологией вы пользовались: JDBC, JPA/Hibernate, даже если и использовали проект Spring Data… Spring Boot наделяет вас уникальным препаратом избавляющим от геморроя всего за одно применение!

Что уж тут говорить, давайте за дело!
Согласно официальной документации, для того чтобы добавить в проект встраиваемую базу данных (embeded databases — google it!), нам необходимо только добавить в помник проекта зависимость драйвера соответствующей базы данных и технологии (у нас data jpa), написать класс-сущность, написать один интерфейс без реализации и написать скрипт заполнения базы данных, всё остальное сделает за нас великий и могучий Spring Boot, у нас будет не встраиваемая база данных, поэтому действий будет чуть больше.

А теперь по конкретным действиям:
Создаем класс сущность, по заданию нужно работать с Юзерами, так и создадим — класс User. Сразу сделаем его персистентным — добавим нужные аннотации.
Создаем директорию domain в той же папке где лежит главный класс.

Должно получиться так:
└── src
    └── main
        └── java
            └── demo(тут ваше название)
                └── controller - о нем речь в конце
                └── domain
                └── repository - о нем речь дальше
                    └── User - новый класс
                └── DemoApplication - главный класс
                └── DemoController - контроллер созданный нами ранее

а вот и сам класс
package demo.domain;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length;

import java.sql.Timestamp;

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue
    private long id;

    @Column(name = "avatar")
    private String url;

    @Length(min = 2)
    private String name;

    @Column(unique = true)
    @Email
    private String email;

    public long getId() {
        return id;
    }

    public User setId(long id) {
        this.id = id;
        return this;
    }

    public String getUrl() {
        return url;
    }

    public User setUrl(String url) {
        this.url = url;
        return this;
    }

    public String getName() {
        return name;
    }

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

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

Открываем pom.xml, находим тег <dependencies>...</dependencies> и вставляем в него еще одну зависимость
<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

вставив эти строки, обновляем зависимости или включаем автоконфигурацию зависимостей мавена, после чего можно перейти назад в класс с Юзером и импортировать аннотации из javax.persistence.*

итак что же мы тут наворотили?!
@Entity — означает что объект этого класса может быть сохранен в базу данных, а сам класс является схемой таблицы, т.е. соответствует структуре таблицы: поля класса — столбцы таблицы.
@Table — говорит о том, какой именно таблице в базе данных соответствует этот класс. В нашем случае название таблицы, не поверите! «users»
@Id — это индентификатор поля,
@GeneratedValue — и ежу понятно, что генерируется значение, в нашем случае имеется ввиду автоинкремент, но нам не нужно об этом беспокоиться и скоро мы узнаем почему!
@Column — применяется если нужно добавить свойств столбцу или если имя имя поля класса не соответствует имени столбца таблицы (например id соответствует, там и мы и не применяем), но можно и применять, за это не поругают.
@Length и @Email — это аннотацим валидации, кликните по ним с нажатым ctrl — провалитесь в сорсы, если они не скачаны — скачайте, почитайте, что там написано.
Вообще, вводите себе за правило постоянно лезть в исходники и читать комментарии авторов кода, смотреть их код, это крайне полезно. Сначала может быть непонятно совсем, но со временем будете понимать всё больше, потом вообще перестанете читать такую лабуду как сейчас читаете — только исходники только хардкор!

В тестовом проекте требуется использовать MySQL, если она у вас не установлена — как ее ставить и основы синтаксиса языка MySQL гуглятся без проблем.
Вообще Spring работает с PostgreSQL, MySQL, Apache Derbi, H2 или HSQLDB (это на момент написания статьи, позднее скорей всего их будет больше)
Включаем очередную зависимость в помник.
<!--Database-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>


Теперь настал черед урока чародейства и волшебства. Создаем новый паккадж repository на том же уровне что и domain. В нем создаем один единственный интерфейс назовем его DemoRepository вот весь его код
package demo.repository;

import demo.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;

interface DemoRepository extends JpaRepository<User, Long> {
}


теперь создадим скрипт заполнения таблички, он обязательно должен называться data.sql, кладем его в папку src/mail/resources
INSERT INTO users (avatar, name, email) VALUES
  ('/pic/ava1', 'Kris', 'kroskross@gmail.com'),
  ('/pic/ava2', 'Josh', 'joshlong@gmail.com');


ну и последний штрих — то, что не нужно делать если используешь встраиваемую базу. в файл application.properties вставляем следующие строки
spring.data.jpa.repositories.enabled=true

spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create

spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.show-sql=true

и снова маленький ньюанс, нужно чтобы у вас была создана база в MySQL, которая называется test, либо вставьте в datasource.url название другой своей созданной базы.

итак резюме:
чтобы прикрутить базу нужно:
1. иметь созданную базу (никаких табличек руками создавать не нужно!, только базу)
2. создать класс-сущность, экземпляры которой будут сохраняться в базу.
3. вставить 2 зависимости в помник
4. создать интерфейс репозитория и унаследоваться от одного из стандартных репозиториев Spring Data Jpa.
5. создать скрипт заполняющий базу.
6. внести настройки базы данных в application.properties

Создается впечатление, что проделан просто громадный объем работы, выдели бы вы сколько нужно писать кода, чтобы подключиться через хибернейт! не говоря уже о jdbc вместе с созданием базы данных через консоль mysql

итак проверяем.
перезапустив приложение мы увидим гораздо больше логов чем, до использования баз данных, но главное должны появиться такие строки:
Hibernate: drop table if exists users
Hibernate: create table users (id bigint not null auto_increment, email varchar(255), name varchar(255), avatar varchar(255), primary key (id))
Hibernate: alter table users add constraint UK_6dotkott2kjsp8vw4d0m25fb7  unique (email)

Да, это спринг Бут создает за нас наши таблицы. Когда я впервые это увидел, я слегка опешил :)
Теперь если у вас меняется доменная модель на этапе проектирования — решили вы Юзеру добавить фамилию или пол или добавить булеан козел он или не козел — нужно просто добавить поле в класс-@Entity и при перезапуске Спринг бут создаст вам таблицу с нужным столбцом, остается только добавить соответствующее поле в скрипт data.sql

теперь давайте выведем наконец в браузер наших юзеров!
создаем паккадж controller, в нем класс UserController, а в нем как мы уже знаем создаем метод с маппингом запроса, который будет возвращать нам список наших Юзеров.
package demo.controller;

import demo.domain.User;
import demo.repository.DemoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping(value = "/users")
public class UserController {

    @Autowired
    DemoRepository demoRepository;

    @RequestMapping(method = RequestMethod.GET)
    public List<User> getAll() {
        return demoRepository.findAll();
    }

}

Тут ничего нового кроме @Autowired, это привязка созданного бина репозитория(если не слышали о таких, почитайте что такое java beans, потом почитайте как они используются в Spring) к ссылке используемой в этом классе.
перезапускаем приложение, пробуем в браузере адрес
localhost:8080/users
ответ должен быть такой
[{«id»:1,«url»:"/pic/ava1",«name»:«Kris»,«email»:«kroskross@gmail.com»},{«id»:2,«url»:"/pic/ava2",«name»:«Josh»,«email»:«joshlong@gmail.com»}]
это наши Юзеры в формате JSON

на сегодня всё. Всем спасибо

п.с. в следующем посте займемся основным функционалом сервера по заданию

11 комментариев

quadlex
Спасибо за интересную тему, жду продолжения.
morror
интересно, а сам бин в файле контекста прописывать не надо? Имеется ввиду Demorepository
naut92
  • naut92
  • 0
  • Комментарий отредактирован 2017-03-19 17:20:02 пользователем naut92
Прекрасно! Спасибо. У меня не запускалось без

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)

была ошибка null-значение в столбце id
ArsTV
Почему не заносит данные в таблицу после её создания?
У меня нет такой строки:
Hibernate: alter table
Как это исправить или где можно настроить?
fatfaggy
  • fatfaggy
  • 0
  • Комментарий отредактирован 2018-02-03 01:00:38 пользователем fatfaggy
ссылку на ваш проект на гитхабе оставьте.
если у вас нет такой строки — значит у вас таблицы не связаны получаются, скорее всего.
но лучше не гадать, а посмотреть код
ArsTV
github.com/ArseniiT/book

Hibernate пересоздаёт таблицу каждый раз при компиляции. И не заполняет eё. И вообще таблица, автоматически созданная Hibernate, не соответствует скрипту data.sql. То есть Hybernate не работает по файлу data.sql. Вот я и думаю, что это где-то настроить можно.
Спасибо.
fatfaggy
у вас в файле application.properties стоит свойство
spring.jpa.hibernate.ddl-auto=create
это значит, что хайбернейт будет при каждом запуске дропать вашу существующую таблицу в базе и создавать новую по классу сущности, которую она видит (в вашем случае, класс Book)
погуглите по ключу свойства этого какие там еще значения могут быть (например, может быть вам update подойдет для тестирования вашей программы больше, чем create)

если таблица в базе получается не такой, как вы хотели бы — значит что-то не так с классом Book.
но так вообще должны быть похожи. а что с автосозданной таблицей по классу Book не так? длина варчаров другая?) или вместо тиниинт — там обычный инт?) почему вообще не воспользоваться было булеаном в данном случае?) если вы будете только 0 и 1 использовать

а почему хайбернейт не подхватывает ваш файл data.sql и не выполняет его — то тоже в гугле можно найти ответ. например как назвать файл со скриптом и куда его положить, чтобы хайбернейт при запуске его выполнял)
fatfaggy
вы уж извините, что я вас в гугл отправляю)) просто я таких моментов тоже в памяти не держу и чтобы вам ответить все — точно так же пошел бы в гугл за информацией :)
ArsTV
Делаю задачу для стажировки. И в комментах прояснили: «Скрипт нужно выполнить один раз в ручном режиме».
Сейчас эта проблема для меня уже не первоочередная.Потом разберусь. Достаточно, создать скрипт и таблицу в ручном режиме. Не нужно с помощью Хибернета, как я думал.

c разными свойствами spring.jpa.hibernate.ddl-auto= уже пробовал. Безуспешно.
По заданию названия колонок в таблице должны быть в одно слово и с кэмэл кейсом. Но Хибернет из имён полей класса переписывает в названия с нижним подчёркиванием. (readAlready --> read_already)

C boolean подумал, что не стоит заморачиваться, если в итоге в таблице MySQL будет храниться TINYINT. Да, пожалуй, байт или булеан будет уместней.

Спасибо за быстрый ответ!
fatfaggy
  • fatfaggy
  • 0
  • Комментарий отредактирован 2018-02-04 04:44:14 пользователем fatfaggy
По заданию названия колонок в таблице должны быть в одно слово и с кэмэл кейсом. Но Хибернет из имён полей класса переписывает в названия с нижним подчёркиванием. (readAlready --> read_already)
ты можешь задать ему в описании своего ентити класса как должна называться таблица, как должны называться поля, итд.
для полей — попробуй указать перед нужным полем аннотацию
@Column(name = "readAlready")
private boolean readAlready;


а с булеаном единственная заморочка — это как добавить значение туда в скрипте) но это тоже гуглится за секунды :)
ArsTV
Решил проблему hibernate с переименованием колонок CamelCase to SNAKE_CASE.
stackoverflow.com/questions/25283198/spring-boot-jpa-column-name-annotation-ignored
Мне помог второй ответ там:
в application.properties добавил две строчки
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.