#CollectionTable does not connect - spring

i have the two tables
Users Table
create table users (
usernumber int unsigned not null auto_increment primary key,
username varchar(32) not null,
password varchar(72) not null,
displayname varchar(32) not null,
birthday datetime not null,
email varchar(255) not null,
firstname varchar(32) not null,
lastname varchar(32) not null
);
Users Locked Table
create table users_locked (
idx int unsigned not null auto_increment primary key,
usernumber int unsigned not null,
start_date datetime not null,
end_date datetime not null,
memo varchar(32) default '' not null,
foreign key (usernumber) references users(usernumber) on delete cascade
);
and i used #CollectionTable to create the entity associated with the two table
following...
Users.java
#Getter
#Builder
#AllArgsConstructor
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#Entity
#Table(name = "users")
#Access(AccessType.FIELD)
public class Users {
#EmbeddedId
private UsersID usernumber;
#Column(name = "username", unique = true)
private String username;
#Column(name = "password")
private String password;
#Column(name = "displayname")
private String displayname;
#Embedded
private PersonalInformation personal;
#ElementCollection(fetch = FetchType.LAZY)
#CollectionTable(name = "users_locked", joinColumns = #JoinColumn(name = "usernumber"))
private List<LockPeriod> locked;
// ....
}
LockPeriod.java
#Getter
#Builder
#EqualsAndHashCode
#AllArgsConstructor
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#Embeddable
public class LockPeriod {
#Column(name = "start_date")
private Date start;
#Column(name = "end_date")
private Date end;
#Column(name = "memo")
private String memo;
//....
}
but the following error occurs and can not be mapped :
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Could not get constructor for org.hibernate.persister.entity.SingleTableEntityPersister
did I do something wrong?
I am sorry for the lack of English.

Related

Why OneToMany JPA association is failing while insert statement executes

Hi below is my schema definition
CREATE TABLE LOANS (
LOAN_ID NUMBER(9,0) PRIMARY KEY,
CORR_ID VARCHAR(5) NULL
);
CREATE TABLE DV_LOAN_PARTICIPANTS (
LOAN_ID NUMBER(9,0) ,
DVP_PARTICIPANT_NAME VARCHAR(50) NOT NULL,
DVP_PARTICIPANT_TYPE VARCHAR(50) NOT NULL,
PRIMARY KEY ("LOAN_ID", "DVP_PARTICIPANT_NAME")
);
LOANS Entity
#Table(name = "LOANS")
#Entity
public class Loans {
#Id
#Column(name = "LOAN_ID")
private Long loanId;
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "LOAN_ID")
#MapKey(name = "dvpParticipantName")
private Map<String, DVLoanParticipants> dvLoanParticipantsMap;
// getter and setters
}
DV_LOAN_PARTICIPANTS Entity
#Table(name = "DV_LOAN_PARTICIPANTS")
#Entity
public class DVLoanParticipants implements Serializable {
#Id
#Column(name = "LOAN_ID")
private Long loanId;
#Id
#Column(name = "DVP_PARTICIPANT_NAME")
private String dvpParticipantName;
#Column(name = "DVP_PARTICIPANT_TYPE")
private String dvpParticipantType;
// getters and setters
}
Service Class is
DVLoanParticipants dvLoanParticipants = new DVLoanParticipants();
dvLoanParticipants.setLoanId(Long.valueOf("196801758"));
dvLoanParticipants.setDvpParticipantName("VKP");
dvLoanParticipants.setDvpParticipantType("Developer");
Loans loanInsert = new Loans();
loanInsert.setLoanId(Long.valueOf("196801758"));
Map<String,DVLoanParticipants> partyMap = new HashMap<>();
partyMap.put("VKP",dvLoanParticipants);
loanInsert.setDvLoanParticipantsMap(partyMap);
repository.save(loanInsert);
But when i am executing the save i am getting error as
NULL not allowed for column "LOAN_ID"; SQL statement:
insert into dv_loan_participants (dvp_participant_type, loan_id, dvp_participant_name) values (?, ?,
?)
Git Hub Code
https://github.com/vinoykp/spring-jpa/tree/master/spring-boot-hibernate-crud-demo
I had the similar question
Why Value is not getting assigned in JPA for insert statement
What is the issue in association?

Hibernate: Find entity from one to many table

I have two tables
CREATE TABLE `heroic_quality`
(
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(515) NOT NULL UNIQUE,
PRIMARY KEY (`id`)
);
CREATE TABLE `hero`
(
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(515) NOT NULL UNIQUE,
`quality_id` INT DEFAULT NULL,
FOREIGN KEY (`quality_id`) REFERENCES heroic_quality (id),
PRIMARY KEY (`id`)
);
And the objects in hibernate are
#Table(name = "heroic_quality")
#Entity(name = "heroic_quality")
public class HeroicQuality
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
protected long id;
#Column(name = "name", nullable = false, unique = true)
private String name;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "id")
#Fetch(FetchMode.SELECT)
private List<Hero> heroes;
//ommited getters and setters for shortness
}
#Table(name = "hero")
#Entity(name = "hero")
public class Hero
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
protected long id;
#Column(name = "name", nullable = false, unique = true)
private String name;
//ommited getters and setters for shortness
}
As you see my Hero class doesn't have reference to heroic quality, and I would like to keep it that way.
Also I have a repository
#Repository
public interface HeroicQualityDAO
extends PagingAndSortingRepository<HeroicQuality, Long>
{
Optional<HeroicQuality> findByName(String name);
List<HeroicQuality> findByOrderByIdDesc();
}
What I would like to do is have a method such as
Optional<HeroicQuality> findByHeroName(String heroName)
Such that if given a name of hero from Hero table I will be able to get heroic quality object.
How can I make such a method?
Is there any way I can get heroic quality object without having a reference to it in the hero object?
How can I go about doing that?
Add the following method to HeroicQualityDAO.
Optional<HeroicQuality> findByHeroesName(String heroName);
If you are not happy with the method name, you can do
#Query("Select h from HeroicQuality hq join hq.heros h where h.name = :name")
Optional<HeroicQuality> findByHeroName(String name);

Handling a oneToMany relationship in Spring boot JPA

In my database I have a user who can have multiple email addresses. An email address can have only one user. I have following two tables in my database to handle this.
CREATE TABLE IF NOT EXISTS w4a_user (
id INTEGER NOT NULL AUTO_INCREMENT,
login_id VARCHAR(100) NOT NULL UNIQUE,
first_name VARCHAR(100),
last_name VARCHAR(100),
division INTEGER NOT NULL,
created_date TIMESTAMP NOT NULL,
last_active DATE,
PRIMARY KEY (id),
FOREIGN KEY (login_id) REFERENCES w4a_authentication_data (login_id) ON DELETE RESTRICT,
FOREIGN KEY (division) REFERENCES w4a_division (id) ON DELETE RESTRICT
);
CREATE TABLE IF NOT EXISTS w4a_email_address(
email_address VARCHAR(100) NOT NULL,
user_id INTEGER NOT NULL,
is_confirmed BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY (email_address),
FOREIGN KEY (user_id) REFERENCES w4a_user (id) ON DELETE CASCADE
);
In my Spring boot application, I have following entity classes to handle this.
User.java
#Entity
#Table(name = "w4a_user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column(name = "first_name")
#Size(max = 100, message = GlobalConstants.ErrorMessageConstants.ERROR_FIRST_NAME_LENGTH_EXCEEDED)
private String firstName;
#Column(name = "last_name")
#Size(max = 100, message = GlobalConstants.ErrorMessageConstants.ERROR_LAST_NAME_LENGTH_EXCEEDED)
private String lastName;
#Column(name = "created_date")
private Date createdDate;
#Column(name = "last_active")
private Date lastActive;
#ManyToOne
#JoinColumn(name = "division", referencedColumnName = "id")
private Division division;
#OneToMany(mappedBy = "userId", cascade = CascadeType.ALL, orphanRemoval = true)
#Size(min = 1)
private List<ContactNumber> contactNumberList;
#OneToMany(mappedBy = "userId", cascade = CascadeType.ALL, orphanRemoval = true)
#Size(min = 1)
private List<EmailAddress> emailAddresses;
.
.
}
EmailAddress.java
#Entity
#Table(name = "w4a_email_address")
public class EmailAddress {
#Id
#Column(name = "email_address")
#Email(message = GlobalConstants.ErrorMessageConstants.ERROR_EMAIL_INCORRECT_FORMAT,
regexp = GlobalConstants.RegexList.EMAIL_REGEX)
#Size(max = 100, message = GlobalConstants.ErrorMessageConstants.ERROR_EMAIL_LENGTH_EXCEEDED)
private String emailAddress;
#ManyToOne
#JoinColumn(name = "user_id", referencedColumnName = "id")
private User userId;
#Column(name = "is_confirmed")
private Boolean isConfirmed;
.
.
}
I use following method to persist entitites to my database.
#PersistenceContext
private EntityManager em;
#Override
public T createEntity(T entity) {
this.em.unwrap(Session.class).save(entity);
return entity;
}
I set email address list in the user entity and perform above method to create a new user.
The issue I have is when adding a user with an email address already used by an existing user. In this case, the database entry for the email address gets updated with the id of the new user. Instead I want to give an error saying the email address is already in use. What is the best way of handling this?

Spring boot Rest create a category which can reference to another category or not

I have an entity class
public class CategoryEntity implements Serializable {
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
#Column(length = 30, nullable = false)
private String categoryKeyId;
#Column(nullable = false)
private String name;
//Here mappedBy indicates that the owner is in the other side
#OneToMany(fetch = FetchType.EAGER, mappedBy = "category", cascade = CascadeType.ALL)
private List<ProductEntity> products;
#ManyToOne(optional = true, fetch = FetchType.LAZY)
private CategoryEntity parent;
// allow to delete also subcategories
#OneToMany(mappedBy="parent", cascade = CascadeType.ALL)
private List<CategoryEntity> subCategories;
}
this class generates this SQL code :
CREATE TABLE `categories` (
`id` bigint(20) NOT NULL,
`category_key_id` varchar(30) COLLATE utf8_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`parent_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FKsaok720gsu4u2wrgbk10b5n8d` (`parent_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
So far, so good it's perfectly what i'm expecting. My issue concerns how to create a new category.
My DTO layer is :
#Getter #Setter
public class CategoryDto implements Serializable {
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private static final long serialVersionUID = 1L;
private long id;
private int parentCategoryId;
private String categoryKeyId;
private String name;
private List<CategoryDto> subCategories;
private CategoryDto parentCategory;
}
I also created 2 Rest Model for creating categories one for the request and the other for the response.
I need to provide a json as entry with the name and the parent category id:
#Getter #Setter
public class CategoryCreateRequestModel {
private String name;
private int parentCategory;
}
And i retrieve a json as output :
#Getter #Setter
public class CategoryCreateRest {
private String categoryKeyId;
private String name;
private CategoryCreateRest parentCategory;
}
My createCategory method returns the output result i expect and takes a CategoryCreateRequestModel as input.
#PostMapping(
consumes = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE },
produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE }
)
public CategoryCreateRest createCategory(#RequestBody CategoryCreateRequestModel categoryCreateRest) throws Exception {
CategoryCreateRest returnValue = new CategoryCreateRest();
if( categoryCreateRest.getName().isEmpty())
throw new NullPointerException(ErrorMessages.MISSING_REQUIRED_FIELDS.getErrorMessage());
ModelMapper modelMapper = new ModelMapper();
CategoryDto categoryDto = modelMapper.map(categoryCreateRest, CategoryDto.class);
CategoryDto createdCategory = categoryService.createCategory(categoryDto);
returnValue = modelMapper.map(createdCategory, CategoryCreateRest.class);
return returnValue;
}
My service layer :
#Override
public CategoryDto createCategory(CategoryDto categoryDto) {
// check if category name and parentId are identicals
if (categoryRepository.findByName(categoryDto.getName()) != null)
throw new ApplicationServiceException("Record already in Database");
ModelMapper modelMapper = new ModelMapper();
CategoryEntity categoryEntity = modelMapper.map(categoryDto, CategoryEntity.class);
// generate categoryKeyId
String categoryKeyId = utils.generateCategoryKeyId(30);
categoryEntity.setCategoryKeyId(categoryKeyId);
CategoryEntity storedCategory = categoryRepository.save(categoryEntity);
CategoryDto returnValue = modelMapper.map(storedCategory, CategoryDto.class);
return returnValue;
}
When i set a new category for example:
{
"name": "catName",
"parentCategoryId": 12
}
or
{
"name": "catName",
"parentCategoryId": null
}
I obtain a 500 error message : could not execute statement; SQL [n/a]; constraint [PRIMARY]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement"
Apparently I have issues with the primary key and I don't see what is going wrong. I should not need to pass an id to this json because it should be automatically generated.

How #RequestBody works

How to get more details:
I am doing simple rest post request from Postman chrome extension.
My controller is :
#Controller
#RequestMapping("/theme")
public class ThemeController {
#RequestMapping(value = "/create", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody
Status addTheme(#RequestBody Theme theme) {
try {
themeServices.addEntity(theme);
return new Status(1, "Theme added Successfully !");
} catch (Exception e) {
// e.printStackTrace();
return new Status(0, e.toString());
}
}
In Theme.java:
#Entity
#Table(name = "theme", uniqueConstraints = { #UniqueConstraint(columnNames = { "theme_id" }) })
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
#NamedQuery(name = "Theme.findAll", query = "SELECT t FROM Theme t")
public class Theme implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "theme_id")
private long themeId;
private String description;
private String name;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "category_id", nullable=true)
private ThemeCategory themeCategory;
In ThemeCategory.java:
#Entity
#Table(name = "theme_category", uniqueConstraints = { #UniqueConstraint(columnNames = { "category_id" }) })
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
#NamedQuery(name = "ThemeCategory.findAll", query = "SELECT t FROM ThemeCategory t")
public class ThemeCategory implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "category_id")
private long categoryId;
private String description;
private String name;
// bi-directional many-to-one association to Theme
// #OneToMany(mappedBy="themeCategory")
#OneToMany(mappedBy = "themeCategory", fetch = FetchType.EAGER)
#Column(nullable = true)
#JsonManagedReference
private Set<Theme> themes;
// bi-directional many-to-one association to ThemeCategory
#ManyToOne
#JoinColumn(name = "parent_category_id", nullable=true)
#JsonBackReference
private ThemeCategory parentThemeCategory;
// bi-directional many-to-one association to ThemeCategory
// #OneToMany(mappedBy="themeCategory")
#OneToMany(mappedBy = "parentThemeCategory", fetch = FetchType.EAGER)
#Column(nullable = true)
#JsonManagedReference
private Set<ThemeCategory> themeCategories;
Theme Category Table:
CREATE TABLE `theme_category` (
`category_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`parent_category_id` smallint(5) unsigned DEFAULT NULL,
`name` varchar(45) NOT NULL,
`description` varchar(1000) DEFAULT NULL ,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`category_id`),
KEY `idx_parent_category_id` (`parent_category_id`),
CONSTRAINT `fk_parent_category_id` FOREIGN KEY (`parent_category_id`) REFERENCES `theme_category` (`category_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=301 DEFAULT CHARSET=utf8;
Theme Table:
CREATE TABLE `theme` (
`theme_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(45) NOT NULL,
`category_id` smallint(5) unsigned NOT NULL,
`file_path` varchar(200) DEFAULT NULL,
`description` varchar(1000) DEFAULT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`theme_id`),
KEY `idx_category_id` (`category_id`),
CONSTRAINT `fk_category_id` FOREIGN KEY (`category_id`) REFERENCES `theme_category` (`category_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=401 DEFAULT CHARSET=utf8;
I am using Postman extension to do a rest post call:
http://localhost:8080/CustomerRegistration/theme/create
Header params:
Content-Type: application/json
Json Body:
{"description":"theme8","name":"theme8","themeCategory":{"categoryId":302, "themes":[],"parentThemeCategory":{}, "themeCategories":[]}}
And tried around 2 hours with multiple ways of body. But it consistently saying:
The server refused this request because the request entity is in a format not supported
by the requested resource for the requested method.
To analyse, I am not getting any thing else. In Eclipse console also not showing anything regarding the this issue.
What is wrong? Is there any tools to create valid requests.

Resources