Задать вопрос

Разъяснение в конфигурации Spring+Hibernate(JPA). Помощь в настройке транзакций. Почему они не проходят?

Приветствую всех! У меня как у новичка в программировании возникли трудности в освоении новых технологий. Переходя с голой Jdbc + сервлеты на Hibernate+Spring, я столкнулся , как мне казалось, с целым айсбергом(google уже весь фиолетовый), и самая большая сложность - правильно соединить все составляющие. Конечно, как говорят, я мог воспользоваться Spring Boot-ом, и он бы решил многие мои проблемы, но все-таки я очень хочу разбираться в том, что я делаю(ведь я уже и так на высоком уровне абстракции). В общем вопрос мой комплексный, и надеюсь на вашу помощь, ребята:

Для начала пару вопросов по конфигурационному файлу, вот мой mvc-spring-dispatcher.xml:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/mydbtest"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="packagesToScan" value="com.springapp.mvc"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

    <bean id="persistenceExceptionTranslationPostProcessor"
          class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

    <tx:annotation-driven transaction-manager="transactionManager"/>


Я очень долго искал лучшее для меня решение, хотел, чтобы Spring инкапсулировал все, и реализацией Jpa был именно Hibernate. Это сейчас еще, если так можно выразиться, актуальное решение, либо есть какой-то более эффективный вариант?

Получается же ведь, что именно это настройка вобрала в себе persistence.xml?

DriverManagerDataSource, я как понимаю тут происходит настройка пула-соединений, т.е фактически маршрут для entityManager до БД, а именно тут происходит настройка количества подключений либо их длительности? Дальше фактически идет настройка провайдера Jpa и все как в persistence.xml, но зачем вводить бин JpaTransactionManager? Что именно он мне дает? Настройка @Transactional? Но разве, если я не буду включать этот бин в конфигурацию и просто оставлю , то разве менеджером транзакций не будет и так прописанный Jpa?

Так же у меня не хочет проходить транзакция(Ниже будет приведен список задействованных классов ):

Person:
import javax.persistence.*;

@Entity
@Table(name = "person")
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    @Column(name = "id" )
    private Integer id;

    @Column(name = "name", length = 40 )
    private String name;
    @Column(name = "email", length = 100 )
    private String email;


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "Person [id=" + id + ", name=" + name + ", email=" + email + "]";
    }
}


PersonDao:
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import com.springapp.mvc.Entities.Person;
import org.springframework.stereotype.Repository;


@Repository("personDao")
public class PersonDao {

    @PersistenceContext
    private EntityManager entityManager;

    public EntityManager getEntityManager() {
        return entityManager;
    }

    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    public void insert(Person person) {
        entityManager.persist(person);
    }

    public List<Person> selectAll() {
        Query query = entityManager.createQuery("");
        List<Person> persons = (List<Person>) query.getResultList();
        return persons;
    }
}


PersonService:
package com.springapp.mvc.Service;

import java.util.List;

import com.springapp.mvc.DAO.PersonDao;
import com.springapp.mvc.Entities.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class PersonService {

    private PersonDao personDao;

    public PersonDao getPersonDao() {
        return personDao;
    }

    @Autowired
    public void setPersonDao(PersonDao personDao) {
        this.personDao = personDao;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void addPerson(Person person) {
        getPersonDao().insert(person);
    }

    public List<Person> fetchAllPersons() {
        return getPersonDao().selectAll();
    }
}


Часть Контроллера :

@RequestMapping(value = "/superData", method = RequestMethod.POST , produces= MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<PeopleEntity> peopleEntity(@RequestBody Person transform) {
        System.out.println(transform);
        PersonService service=new PersonService();
        service.addPerson(transform);
        PeopleEntity peopleEntity = new PeopleEntity();
        peopleEntity.setFirstName("John");
        peopleEntity.setLastName("Dorian");

        return new ResponseEntity<PeopleEntity>(peopleEntity, HttpStatus.OK);
    }


Вот моя таблица:

CREATE TABLE `person` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(40) NOT NULL,
  `email` varchar(100) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8


А вот эту он генерирует автоматически, хотелось бы узнать причину:
CREATE TABLE `hibernate_sequences` (
  `sequence_name` varchar(255) NOT NULL,
  `next_val` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`sequence_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8


Вот в чем суть:
До контроллера доходят параметры: name и email, но дальше случаются следующие вещи:

1) При трансформации информации в класс Person, имени и емэйлу присваиваются соответствующие значения, но id=null. Так же правильно делать? Ведь это значение я оставляю на генерацию в таблице :
@Id
    @GeneratedValue(strategy = GenerationType.TABLE)
И в данном случае оно должно проставить id само в соответствии со счетчиком в таблице?

2) Почему если я не определяю в @Column(name = "email", length = 100 ) длину, то оно изменяет саму таблицу, и делает максимальные значение по 255, если hibernate.hbm2ddl.auto=update? Или этот параметр на это значение не влияет?

3) Почему создается дополнительная таблица(hibernate_sequences), хотя я нигде этого не указываю?

4) Почему не выпадает никакого эксепшена, если транзакция не прошла?

Дальше вопросы про аннотации:

@Repository - она как расширенный @Component, только с расширенными возможностями: выбрасывает свои специальные эксепшены, имеет значение как логический компонент для разработчика(Т.е человек будет видеть, что это класс DAO слоя). Для чего служит значение указанное в аннотации, в данном случае @Repository("personDao")?

@PersistenceContext - как я понял, она автоматически инжектит EntityManager, который я выбрал и настроил тут в бинах: org.springframework.orm.jpa.JpaTransactionManager? Т.е это убирает шаблонные действия по созданию менеджера для транзакций, начиная от createEntityManager, до getTranssaction().begin()?

@Transaction- активируется и в свою очередь комитит все транзакции а потом закрывает? Опционально может и откат делать, в случае неудачи? Что значит (propagation = Propagation.REQUIRED)? Я конечно нашел несколько объяснений, но без примера, не совсем понятно как оно работает и вообще для чего.

И последний вопрос: в слое Dao, когда я хочу выбрать всех пользователей я создаю запрос :
Query query = entityManager.createQuery("from Person"), это язык hql, но почему-то idea не нравится этот запрос, хотя я видел полно примеров где этот способ работал;

Заранее прошу прощения, что вопросов такое множество, и может было бы лучше разделить их на несколько небольших, но мне показался такой комплексный подход наиболее подходящим.
  • Вопрос задан
  • 5630 просмотров
Подписаться 2 Оценить Комментировать
Решения вопроса 1
zolt85
@zolt85
Программист
Я вам не скажу за всю Одессу, но у нас в Сибири считается, что
1) @GeneratedValue(strategy = GenerationType.TABLE) - говорит, что нужно использовать таблицу для генерации PK. Не таблицу, на которую маппится сущность, а просто таблицу. Отсюда и возникает таблица hibernate_sequences. И возникает она из-за hibernate.hbm2ddl.auto=update.
2) Про длину колонки, это опять из-за hibernate.hbm2ddl.auto=update.
3) @Transaction говорит, что метод будет транзакционным. Что это значит. Это значит, что вызов метода будет обернут в proxy объект, у которого будет сессия, и при вызове всех вложенных методов эта сессия будет одна и та же, и при завершении метода транзакция закроется.
4) propagation = Propagation.REQUIRED - это означает, что наличие сессии при вызове метода обязательно, если ее нету, то hibernate попытается ее создать.
5) Откат транзакции (rollbak грубо говоря) происходит, по-умолчанию, только в случае возникновения RuntimeException, как этим управлять написано в документации к hibernate.
6) значение в аннотации @Repository("personDao") задает имя бина.

Теперь по классике надо что-то по советовать. Советую Вам не использовать hibernate.hbm2ddl.auto=update. Лучше посмотрите в сторону liquibase для управления состоянием БД.
И почитайте вводные туториалы по Spring, разберитесь как работает IoC в Spring, как работает AOP в Spring, как Spring управляет бинами. И тогда Вам станет все понятно.

Дерзайте, удачи Вам!
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы