I have an entity with unique constraints. When concurrent requests try to persist entities with the same constrained value, it causes a unique constraint violation.
#Entity
public class Employee {
#Id
#GeneratedValue
private Long id;
#Column(unique = true)
private String name;
}
#Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
Optional<Employee> findByName(String name);
}
...
public Employee findOrCreate(String name) {
Optional<Employee> employee = repository.findByName(name);
if (employee.isPresent()) {
return employee.get();
} else {
Employee newEmployee = new Employee(name);
repository.save(newEmployee);
return newEmployee;
}
}
...
I have tried using #Transactional in various configurations.
What would be the best way to handle this?
Related
Insert JSON values into multiple tables using JPA and spring-boot.
User Table
#Entity
class User {
private #Id #GeneratedValue Long id;
private String name;
#OneToOne(cascade = {
CascadeType.All
})
#JoinColumn(referencedColumnName = "productid")
private Product product;
public User() {}
public User(String name, Product product) {
this.name = name;
this.product = product;
}
}
Product Table
#Entity
class Product {
private #Id #GeneratedValue Long productid;
private String productName;
public Product() {}
public Product(String productName) {
this.productName = productName;
}
}
Repository
#Repository
public interface UserRepo extends JpaRepository < User, Long > {}
Json Input
{
"name": "John",
"product": {
"productName": "Product 1"
}
}
Rest Controller
UserRepo usrRepo;
#PostMapping("/user")
User addEmployee(#RequestBody User user) {
return usrRepo.save(user);
}
When I use the above, both User and Product tables get updated with the new values from JSON. But I want to have the same functionality using #Query. Using the below code, I can update one table but not both.
Help me to insert JSON values into multiple tables using #Query. I am using cockroach db, please suggest if there is any other way to achieve this instead of spring-data-JPA.
Query
#Modifying
#Transactional
#Query(value = "insert into user (name, productid) values (:#{#user.name}, :#{#user.productid})", nativeQuery = true)
void insert(#Param("user) User user);
I need to remove cart object from json, but only in one controller method and that is:
#GetMapping("/users")
public List<User> getUsers() {
return userRepository.findAll();
}
User
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotBlank(message = "Name cannot be empty")
private String name;
#OneToOne
private Cart cart;
}
Cart
#Entity
public class Cart {
#Id
private String id = UUID.randomUUID().toString();
#OneToMany
private List<CartItem> cartItems = new ArrayList<>();
#OneToOne
#JsonIgnore
#OnDelete(action = OnDeleteAction.CASCADE)
private User user;
}
I have done it with simple solution so i loop trough all users, and set their cart to null,and then anotated user entity with #JsonInclude(JsonInclude.Include.NON_NULL)
But i dont think this is propper solution, so im searching for some better solution..
How am i able to do this?
Thanks...
You can create DTO (data transfer object) class like this:
#Data
public class UsersDto {
private Integer id;
private String name;
public UsersDto(User user) {
this.id = user.id;
this.name= user.name;
}
}
and than create List<UsersDto>
#GetMapping("/users")
public List<UsersDto> getUsers() {
List<User> users = userRepository.findAll();
return users
.stream()
.map(o -> new UsersDto(o))
.collect(Collectors.toList());
}
You should use Data Projection.
In your use case, you can use an interface projection:
public interface CartlessUser {
Integer getId();
String getName();
}
And In your repository:
public interface UserRepository extends JpaRepository<User, Integer> {
List<CartlessUser> findAllBy();
}
The interface projection will help generate the sql query for only selecting the id, name fields. This will save you from fetching the Cart data when you're just going to throw it away anyways.
I am working on spring boot application with RestController, Service a Repository and an Entity.
My problem is when I call the web service to save my data in the data base, it seems it works fine and there is no exception thrown but when I check my data base I find that the table was created but I find no data saved. and here is what I get in the output(for each element in my list):
Hibernate:
insert
into
table_name
(columnOne, columnTwo)
values
(?, ?)
Here is my code:
RestController:
#RestController
#RequestMapping(path = "/api/")
public class myController {
#Autowired
private MyService myService;
#PostMapping(path="/inject/{year}")
public void myControllerMethod(#PathParam("year") Year year) {
this.myService.myServiceMethod(year);
}
}
Service:
#Service
public class MyService {
#Autowired
MyRepository myRepository;
public void myServiceMethod(Year year) {
List<MyEntity> myEntityList = this.parseMyEntityList(year);
this.myRepository.save(myEntityList)
}
}
Repository:
#Repository
public interface MyRepository extends CrudRepository<MyEntity, Long>, JpaSpecificationExecutor<InseeLibelle> {
}
Entity:
#Entity
#Table(name = "table_name", indexes = {
#Index(name = "columnOne_idx", columnList = "columnOne"),
#Index(name = "columneTwo_idx", columnList = "columnTwo"),
})
public class MyEntity{
#JsonIgnore
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long columnId;
#Column
private Integer columnOne;
#Column
private String columnTwo;
public Integer getColumnOne() {
return columnOne;
}
public void setColumnOne(Integer columnOne) {
this.columneOne = colmunOne;
}
public String getColumnTwo() {
return columnTwo;
}
public void setColumnTwo(String columnTwo) {
this.columnTwo = columnTwo;
}
}
I tried to add this line in the repository but it does not work too:
<S extends MyEntity> Iterable<S> save(Iterable<S> entities) ;
Perhaps the problem is with the pgAdmin (like my case), it does not show the data but they exist in the database, try findAll method in the repository or check them with select * directly.
I am using Spring JPA to store a many-to-many relationship between User and Service with the table Acquisition. Since the bridge table contains additional columns I modelled it as having two many-to-one relationships. Both are bidirectional. Additionally the Acquisition entity has a one-to-one relationship with ServiceConfiguration.
There is no problem with saving or retrieving any of these entities. However when I try to delete the acquisition like this:
#Override
#Transactional
public void removeUsersServiceAcquisition(Long serviceId, User user) {
Service service = getService(serviceId);
Acquisition acquisition = findAcquisitionByServiceAndUser(service, user);
acquisitionRepository.delete(acquisition.getId());
log.info("\n retrieved acquisition {} ", acquisitionRepository.findOne(acquisition.getId()));
}
The change is not reflected in the database. The subsequent find within the above method returns null. But later in the code and in the database the record exists. There are no exceptions being thrown.
#Entity
#Table(name="ACQUISITION")
public class Acquisition implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
public Long getId() {
return id;
}
#ManyToOne
#JoinColumn(name="user_id")
public User getUser() {
return user;
}
#ManyToOne
#JoinColumn(name="service_id")
public Service getService() {
return service;
}
#OneToOne(mappedBy="acquisition")
public ServiceConfiguration getConfiguration() {
return configuration;
}
}
#Entity
#Table(name="USER")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
public Long getId() {
return id;
}
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval=true)
public Set<Acquisition> getAcquisitions() {
return acquisitions;
}
}
#Entity
#Table(name="SERVICE")
public class Service implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
public Long getId() {
return id;
}
#OneToMany(mappedBy="service", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval= true)
public Set<Acquisition> getAcquisitions() {
return acquisitions;
}
}
#Entity
#Table(name="SERVICE_CONFIGURATION")
public class ServiceConfiguration implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
public Long getId() {
return id;
}
#OneToOne
#JoinColumn(name="acquisition_id")
public Acquisition getAcquisition() {
return acquisition;
}
public void setAcquisition(Acquisition acquisition) {
this.acquisition = acquisition;
}
}
Here is what I did to get this to work.
The remove method changed to first remove the acquisition from both relationships in which it participated:
#Transactional
public void removeUsersServiceAcquisition(Long serviceId, User user) {
Service service = getService(serviceId);
Acquisition acquisition = findAcquisitionByServiceAndUser(service, user);
service.getAcquisitions().remove(acquisition);
user.getAcquisitions().remove(acquisition);
acquisitionRepository.delete(acquisition.getId());
log.info("\n retrieved acquisition {} ", acquisitionRepository.findOne(acquisition.getId()));
}
This resulted in "no Session" Hibernate exception.
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.netstellar.sitesuite.serviceregistry.site.model.User.acquisitions, could not initialize proxy - no Session
Which I dealt with by adding fetch property to the User mapping. Not sure if this is the only way of addressing this exception.
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval=true)
public Set<Acquisition> getAcquisitions() {
return acquisitions;
}
I just want to have a comment for what I've learned from dozens of samples about Generic DAO design-pattern. I added an inheritance hierarchy between POJO classes, DAO interfaces, and DAO implementations please see codes below
Legend:
DAOs (From Parent to children)
DAO implementations (From Parent to Children)
POJO classes (From Parent to Children)
The Data Acess Objects (Interfaces)
The GenericDAO interface
public interface GenericDAO<T> {
... some crud operations common to all objets
}
The PersonDAO interface
public interface PersonDAO<T extends Person> extends GenericDAO<T> {
... some operations unique to a person
}
The StudentDAO interface
public interface StudentDAO extends PersonDAO<Student> {
... some operations unique to a student
}
The Implementations
The GenericDAO Implementation
#Repository("genericDAO")
public class GenericDAOImpl<T extends Person> implements GenericDAO<T> {
private Class<T> type;
#SuppressWarnings("unchecked")
public GenericDAOImpl() {
this.type = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), GenericDAO.class);
System.out.println(type);
}
#Resource(name = "sessionFactory")
protected SessionFactory sessionFactory;
#Transactional
#Override
public Integer save(T entity) {
return (Integer) sessionFactory.getCurrentSession().save(entity);
}
#SuppressWarnings("unchecked")
#Transactional
#Override
public T get(Integer id) {
return (T) sessionFactory.getCurrentSession().get(type, id);
}
}
The PersonDAO implementation
#Repository ("personDAO")
public class PersonDAOImpl<T extends Person> extends GenericDAOImpl<T> implements PersonDAO<T> {
.. implemented methods for person
}
The StudentDAO implementation
#Repository("studentDAO")
public class StudentDAOImpl extends PersonDAOImpl<Student> implements StudentDAO {
.. implemented methods for student
}
The POJO Classes (Hibernate Annotated)
The Person Class (Parent Abstract Class)
#MappedSuperclass
public abstract class Person {
#Id
#GeneratedValue
#Column (name = "id")
private int id;
#Column (name = "name")
private String name;
#Column (name = "age")
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The concrete class (Student)
#Entity
#Table(name = "STUDENT")
public class Student extends Person {
#Column(name = "school")
private String school;
public Student() {
}
public Student(String school) {
this.school = school;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
}
I've been thinking about how am I going to construct a design-pattern between POJOs and DAO objects for days, Until I've come up with these design based on everything I've learned from different resources around the web. I've come up with the idea of DAO and DAO implementation inheritance based on the inheritance of the POJOs.
is this a good practice? reflecting the hierarchy of the POJOs and do it in DAOs?
am I doing something wrong about here with my design? because I have a complete program that
saves and retrieves my objects from the database without any problem
I'm open to any suggestion or corrections. Thank you in advance!!!
Not a comment on the design, but... have you consider using Spring Spring Data Jpa, which allows you to:
write your repository interfaces, including custom finder methods, and Spring will provide the implementation automatically.