timurnav
21 уровень

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

Пост из группы Архив info.javarush.ru
3261 участников
В продолжение топика про полученное мной задание публикую разбор таких вопросов как прикручивание БД к проекту 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> и вставляем в него еще одну зависимость org.springframework.boot spring-boot-starter-data-jpa вставив эти строки, обновляем зависимости или включаем автоконфигурацию зависимостей мавена, после чего можно перейти назад в класс с Юзером и импортировать аннотации из javax.persistence.* итак что же мы тут наворотили?! @Entity - означает что объект этого класса может быть сохранен в базу данных, а сам класс является схемой таблицы, т.е. соответствует структуре таблицы: поля класса - столбцы таблицы. @Table - говорит о том, какой именно таблице в базе данных соответствует этот класс. В нашем случае название таблицы, не поверите! "users" @Id - это индентификатор поля, @GeneratedValue - и ежу понятно, что генерируется значение, в нашем случае имеется ввиду автоинкремент, но нам не нужно об этом беспокоиться и скоро мы узнаем почему! @Column - применяется если нужно добавить свойств столбцу или если имя имя поля класса не соответствует имени столбца таблицы (например id соответствует, там и мы и не применяем), но можно и применять, за это не поругают. @Length и @Email - это аннотацим валидации, кликните по ним с нажатым ctrl - провалитесь в сорсы, если они не скачаны - скачайте, почитайте, что там написано. Вообще, вводите себе за правило постоянно лезть в исходники и читать комментарии авторов кода, смотреть их код, это крайне полезно. Сначала может быть непонятно совсем, но со временем будете понимать всё больше, потом вообще перестанете читать такую лабуду как сейчас читаете - только исходники только хардкор! В тестовом проекте требуется использовать MySQL, если она у вас не установлена - как ее ставить и основы синтаксиса языка MySQL гуглятся без проблем. Вообще Spring работает с PostgreSQL, MySQL, Apache Derbi, H2 или HSQLDB (это на момент написания статьи, позднее скорей всего их будет больше) Включаем очередную зависимость в помник. mysql mysql-connector-java Теперь настал черед урока чародейства и волшебства. Создаем новый паккадж repository на том же уровне что и domain. В нем создаем один единственный интерфейс назовем его DemoRepository вот весь его код package demo.repository; import demo.domain.User; import org.springframework.data.jpa.repository.JpaRepository; interface DemoRepository extends JpaRepository { } теперь создадим скрипт заполнения таблички, он обязательно должен называться 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 getAll() { return demoRepository.findAll(); } } Тут ничего нового кроме @Autowired, это привязка созданного бина репозитория(если не слышали о таких, почитайте что такое java beans, потом почитайте как они используются в Spring) к ссылке используемой в этом классе. перезапускаем приложение, пробуем в браузере адрес
http://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)
  • популярные
  • новые
  • старые
Для того, что бы оставить комментарий вы должны авторизироваться
ArsTV 35 уровень, Ottawa
2 февраля, 19:49
Почему не заносит данные в таблицу после её создания?
У меня нет такой строки:
Hibernate: alter table
Как это исправить или где можно настроить?
fatfaggy 26 уровень, Киев
3 февраля, 01:00
ссылку на ваш проект на гитхабе оставьте.
если у вас нет такой строки — значит у вас таблицы не связаны получаются, скорее всего.
но лучше не гадать, а посмотреть код
ArsTV 35 уровень, Ottawa
3 февраля, 02:46
github.com/ArseniiT/book

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

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

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

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

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

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


а с булеаном единственная заморочка — это как добавить значение туда в скрипте) но это тоже гуглится за секунды :)
ArsTV 35 уровень, Ottawa
9 февраля, 20:37
Решил проблему 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
naut92 29 уровень
19 марта 2017, 17:20
Прекрасно! Спасибо. У меня не запускалось без
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)

была ошибка null-значение в столбце id
morror 39 уровень
9 апреля 2016, 18:21
интересно, а сам бин в файле контекста прописывать не надо? Имеется ввиду Demorepository
quadlex 40 уровень, Москва
12 сентября 2015, 22:00
Спасибо за интересную тему, жду продолжения.