Как сохранить родителя и ребенка в одном кадре (JPA & Hibernate) - PullRequest
0 голосов
/ 06 декабря 2018

Я начинаю показывать вам свой сценарий.

Это мой родительский объект:

@Entity
@Table(name="cart")
public class Cart implements Serializable{  

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

    @OneToMany(mappedBy="cart", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private List<CartItem> cartItems; 

    ...
}

Это мой дочерний объект:

@Entity
@Table(name="cart_item")
public class CartItem implements Serializable{  

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

    @ManyToOne
    @JoinColumn(name="cart_id", nullable=false)
    private Cart cart;

    ...
}

Как вы можете видетьпросматривая базу данных, в таблице cart_item (дочерний объект) поле cart_id имеет внешний ключ к полю id таблицы cart (родительский объект).

enter image description here

Вот как я могу сохранить объект:

1) есть restController, который читает объект JSON:

@RestController
@RequestMapping(value = "rest/cart")
public class CartRestController {

    @Autowired
    private CartService cartService;    

    @RequestMapping(method = RequestMethod.POST)
    @ResponseStatus(value = HttpStatus.CREATED)
    public void create(@RequestBody CartDto cartDto) {
        cartService.create(cartDto);
    }
}

2) Это CartService , это просто Интерфейс :

public interface CartService {  
    void create(CartDto cartDto); 
}

Это реализация CartService:

import org.springframework.transaction.annotation.Transactional;

    @Service
    @Transactional
    public class CartServiceImpl implements CartService {   
        @Autowired
        private CartDao cartDao;

        @Override
        public void create(CartDto cartDto) {
            cartDao.create(cartDto);
        }
    }

CartDao - это просто еще один интерфейс, я покажу вам только его реализацию:

@Repository
public class CartDaoImpl implements CartDao {

    @Autowired 
    private SessionFactory sessionFactory;

    // in this method I save the parent and its children
    @Override
    public void create(CartDto cartDto) {       

        Cart cart = new Cart(); 

        List<CartItem> cartItems = new ArrayList<>();                   

        cartDto.getCartItems().stream().forEach(cartItemDto ->{     
            //here I fill the CartItem objects;     
            CartItem cartItem = new CartItem();         
            ... 
            cartItem.setCart(cart);
            cartItems.add(cartItem);                
        });
        cart.setCartItems(cartItems);

        sessionFactory.getCurrentSession().save(cart);                  
    }
}

Когда я пытаюсь сохранитьновая корзина и ее cart_item s Я получаю эту ошибку:

SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/webstore] threw 
exception [Request processing failed; nested exception is 
org.springframework.orm.hibernate5.HibernateOptimisticLockingFailureException: Object of 
class     
[com.depasmatte.webstore.domain.CartItem] with identifier [7]: optimistic locking failed; 
nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by 
another transaction (or unsaved-value mapping was incorrect) : 
[com.depasmatte.webstore.domain.CartItem#7]] with root cause
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction
 (or unsaved-value mapping was incorrect) : [com.depasmatte.webstore.domain.CartItem#7]

Полагаю, ошибка зависит от того факта, что при Hibernate попытаться сохранить cart_item, id cart еще не существует!

Как правильно сохранить родительский объект и его детектор в кадре?Спасибо

Ответы [ 4 ]

0 голосов
/ 20 апреля 2019

В дискуссии явно отсутствует одна важная вещь, которая очень важна для этого вопроса - кто владеет этим отношением.вы помещаете mappedBy в родительскую сущность, что означает, что владелец этого отношения переходит к дочерней сущности, он должен заполнить это отношение, явно установив свойство, иначе это семейство отношений не будет построено.Поместите аннотацию JoinColumn поверх Parent, это обеспечит, что владелец отношения является родительским, он установит это отношение, когда родительский объект будет сохранен автоматически

0 голосов
/ 06 декабря 2018

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

0 голосов
/ 06 декабря 2018

Вот список правил , которым вы должны следовать, чтобы иметь возможность сохранить родительскую сущность вместе с ее дочерними элементами в одном кадре:

  • каскадный тип PERSIST должно быть включено (CascadeType.ALL тоже хорошо)
  • a двунаправленное отношение должно быть правильно установлено на обе стороны .Например, parent содержит всех дочерних элементов в своем поле коллекции, и каждый дочерний элемент имеет ссылку на своего родительского элемента.
  • В области транзакции выполняется манипулирование данными. НЕТ АВТОМАТИЧЕСКОГО РЕЖИМА РАЗРЕШЕНО.
  • необходимо сохранить только родительский объект вручную (дочерние элементы будут сохранены автоматически из-за каскадного режима)

Проблемы с отображением:

  • удаление @Column(name="id") из обеих сущностей
  • make setter для cartItems private .Поскольку Hibernate использует собственную реализацию List, и вы никогда не должны изменять его напрямую через setter
  • , инициализируйте свой список private List<CartItem> cartItems = new ArrayList<>();
  • , используйте @ManyToOne(optional = false) вместо nullable = false внутри@JoinColumn
  • предпочитают fetch = FetchType.LAZY для коллекций
  • , лучше использовать вспомогательный метод для установки отношений.Например, у класса Cart должен быть метод:

    public void addCartItem(CartItem item){
        cartItems.add(item);
        item.setCart(this);
    }
    

Проблемы проектирования:

  • не рекомендуется передавать DTO вслой DAO.Лучше выполнять преобразование между DTO и сущностями даже выше уровня обслуживания.
  • гораздо лучше избегать такого стандартного метода save с репозиториями Spring Data JPA
0 голосов
/ 06 декабря 2018

Вы проверили этот пост? Строка была обновлена ​​или удалена другой транзакцией (или сопоставление несохраненного значения было неправильным)

Вы можете найти соответствующий ответ в этом, я думаю, что ваша проблема исходит от getCurrentSession, дажеесли вы используете сеансы, потому что hibernate не является потокобезопасным, сеанс по-прежнему является легковесным и не потокобезопасным объектом.Вы должны что-то выкопать отсюда.

Фактически, когда один поток / сеанс сохраняет объект в базе данных, если другой пытается выполнить ту же операцию, это вызовет такого рода ошибки, потому что идентификатор уже существует, поэтому операции невозможны.

Ура!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...