Unique constraint on combination of more than two columns - java

I have two tables book and page
I' trying to setup a bidirectional one to many relation between them.
page has unique constraint on multiple columns which isn't working correctly. When i insert book_item with unique page_item it works as its supposed to but when i try to delete the book which should also delete the pages, i get unique constraint violation exception which i don't really understand.
i'm using jpa repository to perform insert/delete.
When i remove #UniqueConstraint the delete works but also the constraint doesn't so i don't really understand what i'm doing wrong.
#Entity
#Table(name = "book")
#NoArgsConstructor
#RequiredArgsConstructor
public class Book implements Serializable {
#Id
#GeneratedValue(generator = "book_uuid")
#GenericGenerator(name = "book_uuid", strategy = "org.hibernate.id.UUIDGenerator")
#Column(name = "id", updatable = false, nullable = false, columnDefinition = "VARCHAR(255)")
#Type(type="uuid-char")
#Getter
private UUID id;
#OneToMany(mappedBy = "book", cascade = CascadeType.ALL, orphanRemoval = true)
#Getter
#Setter
private List<Page> pages;
}
#Entity
#Table(name = "page", uniqueConstraints = #UniqueConstraint(columnNames = {"book_id", "chapter", "volume", "name"}))
#NoArgsConstructor
#RequiredArgsConstructor
public class Page implements Serializable {
#Id
#GeneratedValue(generator = "page_uuid")
#GenericGenerator(name = "page_uuid", strategy = "org.hibernate.id.UUIDGenerator")
#Column(name = "id", updatable = false, nullable = false, columnDefinition = "VARCHAR(255)")
#Type(type="uuid-char")
#Getter
private UUID id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "book_id", nullable = false)
#Getter
#Setter
#NonNull
private Book book;
#Column(name = "chapter", nullable = false)
#Getter
#Setter
private int chapter = 0;
#Column(name = "volume", nullable = false)
#Getter
#Setter
private int volume = 0;
#Column(name = "name", nullable = false)
#Getter
#Setter
#NonNull
private String name;
}
// Code used for insertion/update /deletion
#Repository
public interface BookRepository extends JpaRepository<Book, UUID> {
}
public void test() {
Book book = bookRepository.findById("9c7b2ab2-1c78-4e9f-adc5-99c7da42a7c6");
List<Page> pages = IntStream.range(0, 5)
.mapToObj(i -> new Page(book, "Page" + i, ".png"))
.collect(Collectors.toList());
book.setPages(pages);
bookRepository.save(book);
bookRepository.delete(book);
The error message i get :
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["UKOBQWYIY89PIIE6GP2NB4PVQ8W_INDEX_2 ON PUBLIC.PAGE(BOOK_ID, CHAPTER, VOLUME, NAME) VALUES ('acb0bb92-be2f-4dd3-9653-98f8d890e6b4', 0, 0, 'Page0', 1)"; SQL statement:
insert into page (book_id, chapter, extension, name, volume, id) values (?, ??, ?, ?, ?, ?) [23505-197]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement

You could try deleteById
deleteById("9c7b2ab2-1c78-4e9f-adc5-99c7da42a7c6");

Related

JPA #ManyToMany association not fetching owner class' collection

I have a simple Many-to-Many relationship between a User and Role entity. The tables are correctly created. Every time I add a role to a user, the USER_ROLE table is correctly updated with the user_id and role_id. But when fetching all the users, the roles collection is always empty.
User.java
#Entity
#Table(name = "USER")
#Builder
#Data
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(callSuper = true)
public class User extends BaseEntity {
#Column(
name = "USERNAME",
unique = true,
nullable = false
)
private String username;
#Column(
name = "PASSWORD",
nullable = false
)
private String password;
#Column(name = "DEPOSIT")
private int deposit;
#ManyToMany(
fetch = FetchType.EAGER,
cascade = CascadeType.PERSIST
)
#JoinTable(
name = "USER_ROLE",
joinColumns = #JoinColumn(name = "USER_ID"),
inverseJoinColumns = #JoinColumn(name = "ROLE_ID")
)
private Collection<Role> roles = new HashSet<>();
}
Roles.java
#Entity
#Table(name = "ROLE")
#Builder
#Data
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(callSuper = true)
public class Role extends BaseEntity {
#Column(
name = "NAME",
nullable = false,
unique = true
)
private String name;
#ManyToMany(mappedBy = "roles")
#JsonIgnore
private Collection<User> users = new HashSet<>();
}
BaseEntity.java
#MappedSuperclass
public abstract class BaseEntity implements Serializable {
#Id
#GeneratedValue(
generator = "UUID"
)
#GenericGenerator(
name = "UUID",
strategy = "org.hibernate.id.UUIDGenerator"
)
#Column(
name = "ID",
updatable = false
)
private UUID id;
}
I saw this question many times on stackoverflow. I've tried multiple solutions but none of them worked and i can't seem to find the issue.
Edit: I have no ideas why, but it seems that the issue was coming from the fact that I was using UUID as primary keys. Now that I changed it to long values, it's working properly.

Null point exception on set spring JPA relationship

I have already seen topics with this question, but they did not help me. Maybe I didn't see something.
Below I attach the code and the error.
#Entity
#Table(name = "department")
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#EqualsAndHashCode
public class Department {
#Id
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "uuid2")
#Column(length = 36, nullable = false, updatable = false)
private UUID id;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "description", nullable = false)
private String description;
#OneToMany(mappedBy = "department", cascade = CascadeType.ALL, fetch = FetchType.LAZY,
orphanRemoval = true)
private Set<User> userSet = new HashSet<>();
}
AND user
#Entity
#Table(name = "user")
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#EqualsAndHashCode
public class User {
#Id
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "uuid2")
#Column(length = 36, nullable = false, updatable = false)
private UUID id;
private String firstName;
private String lastName;
private Integer age;
#ManyToOne
#JoinColumn(name = "dep_id", nullable = true)
private Department department;
#ManyToMany
#JoinTable(
name = "device_devices",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "device_id"))
Set<Device> devices = new HashSet<>();
}
and service
#Override
public List<DepartmentDto> getAllDepartment() {
List<Department> all = departmentRepository.findAll();
return all.stream().map(mapper::toDepartmentDto).collect(Collectors.toList());
}
#Override
public UUID createDepartment(DepartmentDto departmentDto) {
Department entity = mapper.DtoToDepartment(departmentDto);
return departmentRepository.save(entity).getId();
}
#Override
public void deleteDepartment(UUID id) {
departmentRepository.deleteById(id);
}
#Override
#Transactional
public void addUserToDepartment(UUID departmentId,UUID userId){
Department department = departmentRepository.findById(departmentId).orElseThrow(DepartmentNotFoundException::new);
User user = userRepository.findById(userId).orElseThrow(UserNotFoundException::new);
department.getUserSet().add(user); // ERROR java.lang.NullPointerException: null
user.setDepartment(department);
}
I think I did something wrong. I tried writing Cascade.ALL but it didn't help me. I don't think I fully understand the concept of a link collection yet. I don't like that in my code, I add the user to the department, and then I add the department to the user. Probably it can be done in one action.
Task.
I want to make a department and 2 functions. Add a user to the department and remove users from the department. in such a way that the contempt of the users from the department the user himself was not removed.
I will be glad to hear your comments
if you use #Builder annotation on top of the class
and you want to set default value for a field,
you must put annotation #Builder.Default on top of field
#Builder.Default
Set<Device> devices = new HashSet<>();
otherwise devices will always be NULL when the builder builds the object

Create a mapping table with hibernate and retrieve values by columns

i am working on Spring Data JPA with Hibernate. I want to create a mapping table using hibernate automatically and want to retrieve the values based on the id's of each individual tables.
CustomerEntity.java:
#Getter
#Setter
#Table(name = "customers")
#Entity
public class CustomerEntity {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
#Column(name = "customerId", updatable = false, nullable = false)
private UUID customerId;
#ManyToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "customerEntities")
private List<ItemEntity> itemEntities;
//More customer fields
}
ItemEntity.java
#Getter
#Setter
#Table(name = "items")
#Entity
public class ItemEntity {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
#Column(name = "itemId", updatable = false, nullable = false)
private UUID itemId;
#ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "customerId", nullable = false)
private List<CustomerEntity> customerEntities;
//More item fields
}
Now i want to create a mapping table with manyToMany for these two based on the id's
something like below:
MappingTable:
id // Primary Key
customerId // primary key from customer table
itemId // primary key from item table
and i want to use customerId or itemId (depends on which i have available) to find the records within the mapping table.
Can any one please provide any suggestions would be helpful here. TIA.

ORA-01722: invalid number when using Hibernate

I have an entity Job as below.
#Entity
#Getter
#Setter
#NoArgsConstructor
#Immutable
#ToString
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
#Table(name = "JOB")
public class Job extends BaseEntity implements IEntity, IDto {
#Id
#Column(name = "JOB_ID", unique = true, nullable = false)
private Long id;
#Column(name = "PRINT_JOB_ID", length = 30)
private String printJobId;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "PRINT_JOB_ID", nullable = false, insertable = false, updatable = false)
private Set<PrintFile> printFileInfos = new HashSet<PrintFile>();
}
I also have another entity PrintFile.
#Entity
#Getter
#Setter
#NoArgsConstructor
#Immutable
#Table(name = "PRINT_FILE")
public class PrintFile implements Serializable {
#Id
#Column(name = "FILE_ID", unique = true, nullable = false, length = 50)
private String fileId;
#Column(name = "PRINT_JOB_ID", nullable = false, length = 30)
private String printJobId;
}
Here are my tables.
Job
JOB_ID NOT NULL NUMBER
PRINT_JOB_ID VARCHAR2(30)
Print_File
PRINT_JOB_ID NOT NULL VARCHAR2(30)
FILE_ID NOT NULL VARCHAR2(50)
When trying fetch Job data using Sprint boot rest API, I'm getting java.sql.SQLSyntaxErrorException: ORA-01722: invalid number error. All the dataype mapping seems to be correct, what else could have gone wrong ?
EDIT:
The Job entity fetches without any issues when I get rid of the join. i.e, the entire declaration of printFileInfos in Job entity. This makes me think the issue is either with the join or in PrintFile entity.
I would recommend you to try below given code. After adding referencedColumnName attribute, it worked for me.
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "PRINT_JOB_ID", referencedColumnName = "PRINT_JOB_ID", nullable = false, insertable = false, updatable = false)
private Set<PrintFile> printFileInfos = new HashSet<PrintFile>();

Java Persistence Query Language JOIN

I have two tables :
A(bigint id, ...)
B(bigint id, varchar name, bigint id_A)
and now I want get all rows from A which exists in B (and those rows in B have name eg Andy)
Plase help me create dynamic query
class A
#Entity
#Table(name = "A", schema = "mySchema")
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public class A{
#Id
private Long id;
}
class B
#Entity
#Table(name = "B",
schema = "mySchema",
uniqueConstraints = { #UniqueConstraint(columnNames = {
"some_id", "id_A" }) })
public class B{
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "Seq")
#SequenceGenerator(name = "Seq", sequenceName = "mySchema.mySeq")
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_A", nullable = false)
private A a;
#Column(name = "id_A", updatable = false, insertable = false)
private Long IdA;
}
There are several weird parts. e.g. this:
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_A", nullable = false)
private A a;
#Column(name = "id_A", updatable = false, insertable = false)
private Long IdA;
With the #JoinColumn annotation you are telling the JPA provider that it should use the specified column for internal mapping, but with the IdA field, you are trying to manage the column yourself. Which is it going to be?

Categories