I want to use check constraint to verify if there are more students in the subject more than vacancies. These are the entities:
SubjectOffer
#Entity
#SequenceGenerator(name = "SUBJECT_OFFER_SEQ", sequenceName = "SUBJECT_OFFER_SEQ")
#Table(name = "SUBJECT_OFFER", uniqueConstraints = {
#UniqueConstraint(name = "UQ_SUBJECT_OFFER_COURSE_SUBJECT_SEMESTER_CLASS", columnNames = {"COURSE_ID", "SUBJECT_ID", "SEMESTER", "CLASS_NUMBER"})})
#Check(constraints = "COUNT(STUDENT_SUBJECT_ID) <= VACANCIES")
public class SubjectOffer {
#Id
#GeneratedValue(generator = "SUBJECT_OFFER_SEQ")
#Column(name = "SUBJECT_OFFER_ID", nullable = false)
private Long id;
#OneToMany(fetch = FetchType.LAZY, orphanRemoval = true, cascade = CascadeType.ALL)
#JoinColumn(name = "STUDENT_SUBJECT_ID")
private Set<StudentSubject> studentSubjects = new HashSet<>();
//other attributes
#Column(name = "VACANCIES", nullable = false)
private int vacancies;
}
StudentSubject
#Entity
#Table(name = "STUDENT_SUBJECT")
public class StudentSubject {
#EmbeddedId
private StudentSubjectId id = new StudentSubjectId();
#MapsId("studentId")
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "STUDENT_ID", nullable = false)
private Student student;
#MapsId("subjectOfferId")
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "SUBJECT_OFFER_ID", nullable = false)
private SubjectOffer subjectOffer;
#Column(name = "SEMESTER", nullable = false)
private int semester;
#Column(name = "GRADE")
private BigDecimal grade;
}
I also tried column definition in Set #JoinColumn but it didn't work
SQL check constraints only work on a single table. What you want is a so called SQL assertion constraint, but no database implements that. The best you can do is to pre-create rows for the amount of vacancies and just assign students to these rows without ever creating more rows. This way, you can make sure that you only assign as many students as there are vacancies, given that you use optimistic/pessimistic locking when assigning a student.
Related
I have 3 tables as #Entity, and 2 join tables in my spring + hibernate app.
In one of join table i have extra column. I want to take info from this info column when i take info from my main table.
Main table code:
#Entity
#Table(name = "items")
public class Items {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "crafts"
,joinColumns = #JoinColumn(name = "item_id")
,inverseJoinColumns = #JoinColumn(name = "plot_id"))
private Set<Plots> plotInfo = new HashSet<>();
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "item_materials"
,joinColumns = #JoinColumn(name = "item_id")
,inverseJoinColumns = #JoinColumn(name = "material_id"))
private Set<Materials> materialsInfo = new HashSet<>();
Table item_materials have this columns "id, item_id(fkey), material_id(fkey), expense" and one of this which names as "expense" i need to have in my final result.
How can i code my class to have "expense" in my result?
I read about #embeddable but still dont understand how to use in my project.
Don't use a #ManyToMany association. Map the join table as entity and model it similar to this:
#Entity
#Table(name = "items")
public class Items {
#OneToMany(mappedBy = "item")
private Set<Crafts> plotInfo = new HashSet<>();
}
#Entity
#Table(name = "plots")
public class Plots {
#OneToMany(mappedBy = "plot")
private Set<Crafts> items = new HashSet<>();
}
#Entity
#Table(name = "crafts")
public class Crafts {
#EmbeddedId
private CraftsId id;
#ManyToOne
#JoinColumn(name = "item_id", insertable = false, updatable = false)
private Items item;
#ManyToOne
#JoinColumn(name = "plot_id", insertable = false, updatable = false)
private Plots plot;
}
#Embeddable
public class CraftsId {
#Column(name = "item_id")
private Integer itemId;
#Column(name = "plot_id")
private Integer plotId;
// equals + hashCode
}
When saving my entities, child entities that work through the #OneToMany relationship are not saved to their tables. I can’t understand what’s the matter.
Employee:
#Entity
#Table(name = "EMPLOYEE", schema = PUBLIC)
public class Employee {
private String name;
private String lastname;
#OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, orphanRemoval = true)
List<EmployeePhoneNumber> employeePhoneNumbers = new ArrayList<>();
}
EmployeePhoneNumber:
#Entity
#Table(name = "EMPLOYEE_PHONES", schema = PUBLIC)
public class EmployeePhoneNumber {
#Id
#SequenceGenerator(allocationSize = 1, name = "SEQ_EMPLOYEE_PHONES", schema = PUBLIC,
sequenceName = "EMPLOYEE_PHONES_ID_SEQ")
#GeneratedValue(generator = "SEQ_EMPLOYEE_PHONES", strategy = GenerationType.SEQUENCE)
#Column(name = "ID", unique = true, nullable = false)
private Long id;
#ManyToOne
#JoinColumn(name = "employee_id", referencedColumnName = "id",
nullable = false, insertable = false, updatable = false)
private Employee employee;
#Column(name = "PHONE_NUMBER", unique = true, nullable = false)
private String phoneNumber;
#Enumerated(EnumType.STRING)
#Column(name = "NUMBER_TYPE", nullable = false)
private PhoneNumberType phoneNumberType;
}
How I set those fields and then save the entity:
EmployeePhoneNumber workPhone = new EmployeePhoneNumber();
workPhone.setPhoneNumber(workPhone);
workPhone.setPhoneNumberType(PhoneNumberType.WORK_PHONE);
EmployeePhoneNumber mobilePhone = new EmployeePhoneNumber();
mobilePhone.setPhoneNumber(mobilePhone);
mobilePhone.setPhoneNumberType(PhoneNumberType.MOBILE_PHONE);
EmployeePhoneNumber corporatePhone = new EmployeePhoneNumber();
corporatePhone.setPhoneNumber(corporatePhoneNumber);
corporatePhone.setPhoneNumberType(PhoneNumberType.CORPORATE_PHONE);
List<EmployeePhoneNumber> employeePhoneNumbers = employee.getEmployeePhoneNumbers();
employeePhoneNumbers.add(workPhone);
employeePhoneNumbers.add(mobilePhone);
employeePhoneNumbers.add(corporatePhone);
employee.setEmployeePhoneNumbers(employeePhoneNumbers);
employeeRepository.save(employee);
Upon completion of the method, I do not have a single error, everything works out correctly, only the tables are not filled - why?
You must also set the Employee reference in EmployeePhoneNumber because Hibernate will use this to save it.
workPhone.setEmployee(employee);
mobilePhone.setEmployee(employee);
corporatePhone.setEmployee(employee);
The best solution would be to create an addEmployeePhoneNumber method on the Employee like this:
public void addEmployeePhoneNumber(EmployeePhoneNumber phoneNumber) {
phoneNumber.setEmployee(this);
employeePhoneNumbers.add(phoneNumber);
}
That way you will not forget to set both sides of the relationship.
I did googled a lot, still dont find any solution hence posting a question here..
I am developing Many-To-Many relationship example using lombok. I just want to create argument constructor for only two fields out of four. How we can do that ?
#Data
#Entity
#Table(name = "stock")
public class Stock implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "STOCK_ID", unique = true, nullable = false)
private Integer stockId;
#Column(name = "STOCK_CODE", unique = true, nullable = false, length = 10)
private String stockCode;
#Column(name = "STOCK_NAME", unique = true, nullable = false, length = 20)
private String stockName;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "stock_category", joinColumns = {
#JoinColumn(name = "STOCK_ID", nullable = false, updatable = false)},
inverseJoinColumns = {#JoinColumn(name = "CATEGORY_ID", nullable = false, updatable = false)})
private Set<Category> categories = new HashSet<Category>(0);
}
Category
#Data
#RequiredArgsConstructor(staticName = "of")
#Entity
#Table(name = "category")
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "CATEGORY_ID", unique = true, nullable = false)
private Integer categoryId;
#Column(name = "NAME", nullable = false, length = 10)
#NonNull
private String name;
#Column(name = "[DESC]", nullable = false)
#NonNull
private String desc;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "categories")
private Set<Stock> stocks = new HashSet<Stock>(0);
}
App.java
Why cant I set the limitted field constructor
public class App {
public static void main(String[] args) {
System.out.println("Hello World!");
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
Stock stock = new Stock();
stock.setStockCode("7052");
stock.setStockName("PADINI");
Category category1 = new Category("CONSUMER", "CONSUMER COMPANY");
Category category2 = new Category("INVESTMENT", "INVESTMENT COMPANY");
Set<Category> categories = new HashSet<Category>();
categories.add(category1);
categories.add(category2);
stock.setCategories(categories);
session.save(stock);
session.getTransaction().commit();
System.out.println("Done");
}
}
The reason is that
If staticName set, the generated constructor will be private, and an additional
static 'constructor' is generated with the same argument list that
wraps the real constructor.
Please, don't forget about #NoArgsConstructor because Hibernate needs it.
I have a database with several entities, in particular Book and User. Between them there exists a ManyToMany relationship like this:
Book:
#Entity
#Table(name = "Books")
public class Book implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "bookId", nullable = false, unique = true)
private Long id;
#Column(name = "title", nullable = false)
private String title;
#Column(name = "price", nullable = false)
private int price;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "User_Book",
joinColumns = #JoinColumn(name = "bookId"),
inverseJoinColumns = #JoinColumn(name = "userId"))
private Set<UserAccount> users;
User:
#Entity
#Table(name = "UserAccounts")
public class UserAccount implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "userId", nullable = false, unique = true)
private Long id;
#Column(name = "username", nullable = false, unique = true)
private String username;
#Column(name = "password", nullable = false)
private String password;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "User_Book",
joinColumns = #JoinColumn(name = "userId"),
inverseJoinColumns = #JoinColumn(name = "bookId"))
Set<Book> purchasedBooks;
Everything works fine, the table User_Book is indeed created in the database. The problem seems to be related to the access of this Table.
For example,
Query query = entityManager.createQuery("SELECT u FROM User_Book u");
keeps telling me the following:
The abstract schema type 'User_Book' is unknown
So, shall I create from scratch the User_Book entity? Will it get automtically populated like now, that is, whenever a user buys a book, will this purchase be recorded in the table?
User_Book is not an entity. Therefore you cannot use createQuery, BUT you can use createNativeQuery to execute a SQL query:
Query query = entityManager.createNativeQuery("SELECT * FROM User_Book");
The result will be List<Object[]>
I have a LibraryModel class, a LibraryImage class and a LibraryAttribute class. A LibraryModel can have an arbitrary number of LibraryImages and LibraryAttributes.
The error that I get:
org.hibernate.MappingException: Foreign key (FKmbn4xh7xdxv371ao5verqueu3:library_item_attribute [LIBRARY_ITEM_ATTRIBUTE_ID])) must have same number of columns as the referenced primary key (library_item_attribute [LIBRARY_ITEM_ID,LIBRARY_ITEM_ATTRIBUTE_ID])
Here are my annotated Objects:
Library Model:
#Entity
#Table(name = "library_item", uniqueConstraints = {
})
#Inheritance(strategy = InheritanceType.JOINED)
public class LibraryItemModel implements LibraryItem{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "LIBRARY_ITEM_ID", unique = true, nullable = false)
private Integer libraryItemId;
#Column(name = "ITEM_TITLE", unique = false, nullable = false)
private String itemTitle;
#Column(name = "IS_PARENT", nullable = false)
private Boolean isParent;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="LIBRARY_ID", nullable = false)
private LibraryModel libraryModel;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "ITEM_LISTING",
joinColumns = {#JoinColumn(name = "PARENT_LIB_ITEM_ID", nullable=false)},
inverseJoinColumns = {#JoinColumn(name="CHILD_LIB_ITEM_ID", nullable = false)})
private Set<LibraryItemModel> itemChildren = new HashSet<>();
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "itemChildren")
private Set<LibraryItemModel> itemParents = new HashSet<>();
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "LIBRARY_ITEM_IMAGE",
joinColumns = { #JoinColumn(name = "LIBRARY_ITEM_ID", nullable=false)},
inverseJoinColumns = { #JoinColumn(name="LIBRARY_IMAGE_ID", nullable = false)})
private Set<LibraryImage> itemImages = new HashSet<>();
#OneToMany(fetch = FetchType.LAZY, mappedBy = "rootLibraryItemModel")
private Set<LibraryModel> libraries = new HashSet<>();
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "LIBRARY_ITEM_ATTRIBUTE",
joinColumns = { #JoinColumn(name = "LIBRARY_ITEM_ID", nullable =false)},
inverseJoinColumns = { #JoinColumn(name="LIBRARY_ITEM_ATTRIBUTE_ID", nullable = false)})
private Set<LibraryItemAttribute> libraryItemAttributes = new HashSet<>();
}
LibraryImage:
#Entity
#Table(name = "library_image", uniqueConstraints = {
})
public class LibraryImage {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "LIBRARY_IMAGE_ID", unique = true, nullable = false)
private Integer libraryImageId;
#Column(name = "IMAGE_LOCATION")
private String imageLocation;
#Column(name = "IMAGE_TITLE")
private String imageTitle;
#Enumerated(EnumType.STRING)
private LibraryImageType imageType;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name="LIBRARY_ITEM_ID", nullable = false)
private LibraryItemModel libraryItemModel;
}
Library Attribute:
#Entity
#Table(name = "library_item_attribute", uniqueConstraints = {
})
public class LibraryItemAttribute {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "LIBRARY_ITEM_ATTRIBUTE_ID", unique = true, nullable = false)
private Integer libraryItemAttributeId;
#Column(name = "ATTRIBUTE_NAME")
private String attributeName;
#Column(name = "ATTRIBUTE_VALUE")
private String attributeValue;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name="LIBRARY_ITEM_ID", nullable = false)
private LibraryItemModel libraryItemModel;
}
This is really frustrating as the LibraryImage class is mapped without problems and doesn't throw any errors, but the LibraryAttribute class which is annotated in an identical way to the LibraryImage class is throwing this error.
Can someone please have a look and let me know what my problem is?
Found the problem.
In the LibraryItemModel class I defined the Join table with the LibraryItemAttribute to be called "LIBRARY_ITEM_ATTRIBUTE", which is the name of the table of the Library item attributes.
The join table is a different table and should have a different table name.
For the Library Image table above, the image table is called library_image, while the join table is called LIBRARY_ITEM_IMAGE