I'm following the Learn Spring 5 etc on udemy and I'm at the part where we test our application. Everything worked fine till now, i was able to connect to the postgreSQL database and all but now I'm stuck at this test failing since 2 days.
I don't understand what is causing the Test to fail. The application run but the test doesn't. Here it is the test class:
package com.ghevi.dao;
import com.ghevi.pma.ProjectManagementApplication;
import com.ghevi.pma.dao.ProjectRepository;
import com.ghevi.pma.entities.Project;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlGroup;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertEquals;
#ContextConfiguration(classes= ProjectManagementApplication.class)
#RunWith(SpringRunner.class)
#DataJpaTest // for temporary databases like h2
#SqlGroup({
#Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:schema.sql", "classpath:data.sql"}),
#Sql(executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, scripts = "classpath:drop.sql")
})
public class ProjectRepositoryIntegrationTest {
#Autowired
ProjectRepository proRepo;
#Test
public void ifNewProjectSaved_thenSuccess(){
Project newProject = new Project("New Test Project", "COMPLETE", "Test description");
proRepo.save(newProject);
assertEquals(5, proRepo.findAll().size());
}
}
And this is the stack trace:
https://pastebin.com/WcjNU76p
Employee class (don't mind the comments, they are probably garbage):
package com.ghevi.pma.entities;
import javax.persistence.*;
import java.util.List;
#Entity
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "employee_seq") // AUTO for data insertion in the class projmanagapplication (the commented out part), IDENTITY let hibernate use the database id counter.
private long employeeId; // The downside of IDENTITY is that if we batch a lot of employees or projects it will be much slower to update them, we use SEQUENCE now that we have schema.sql (spring does batch update)
private String firstName;
private String lastName;
private String email;
// #ManyToOne many employees can be assigned to one project
// Cascade, the query done on projects it's also done on children entities
#ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.PERSIST}, // Standard in the industry, dont use the REMOVE (if delete project delete also children) or ALL (because include REMOVE)
fetch = FetchType.LAZY) // LAZY is industry standard it loads project into memory, EAGER load also associated entities so it slows the app, so we use LAZY and call child entities later
//#JoinColumn(name="project_id") // Foreign key, creates a new table on Employee database
#JoinTable(name = "project_employee", // Merge the two table using two foreign keys
joinColumns = #JoinColumn(name="employee_id"),
inverseJoinColumns = #JoinColumn(name="project_id"))
private List<Project> projects;
public Employee(){
}
public Employee(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public List<Project> getProjects() {
return projects;
}
public void setProjects(List<Project> projects) {
this.projects = projects;
}
/* Replaced with List<Project>
public Project getProject() {
return project;
}
public void setProject(Project project) {
this.project = project;
}
*/
public long getEmployeeId() {
return employeeId;
}
public void setEmployeeId(long employeeId) {
this.employeeId = employeeId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Also this is the schema.sql where i reference those sequences, since this file is run by the test, i have just noticed that IntelliJ mark some errors in this file. For example it mark red some spaces and the T of TABLE saying:
expected one of the following: EDITIONING FORCE FUNCTION NO OR PACKAGE PROCEDURE SEQUENCE TRIGGER TYPE VIEW identifier
CREATE SEQUENCE IF NOT EXISTS employee_seq;
CREATE TABLE IF NOT EXISTS employee ( <-- here there is an error " expected: "
employee_id BIGINT NOT NULL DEFAULT nextval('employee_seq') PRIMARY KEY,
email VARCHAR(100) NOT NULL,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL
);
CREATE SEQUENCE IF NOT EXISTS project_seq;
CREATE (the error i described is here -->) TABLE IF NOT EXISTS project (
project_id BIGINT NOT NULL DEFAULT nextval('project_seq') PRIMARY KEY,
name VARCHAR(100) NOT NULL,
stage VARCHAR(100) NOT NULL,
description VARCHAR(500) NOT NULL
);
CREATE TABLE IF NOT EXISTS project_employee ( <--Here again an error "expected:"
project_id BIGINT REFERENCES project,
employee_id BIGINT REFERENCES employee
);
You never tell it to about the sequence, just what the generator is called
Try
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "employee_generator")
#SequenceGenerator(name = "employee_generator", sequenceName = "employee_seq", allocationSize = 1)
I had the same issue. Adding the below annotation resolved it. #SequenceGenerator(name = "employee_seq", allocationSize = 1)
Perhaps, there is something wrong with the generator definition in the employee entity.
The "generator" must be the "name" of the SequenceGenerator, not the name of other things such as the sequence. Maybe Because you gave the name of the sequence, and did not have a generator with that name it used the default preallocation which is 50.
Also, the strategy should be SEQUENCE, but isn't required if you define the generator, it is only relevant when you don't define the generator.
By default allocationSize parameter in SequenceGenerator is set to be 50. This problem arises when your sequence increment mismatches. You can either change the sequence increment value or assign allocationSize size as per your requirement.
Related
I am trying to add few initial values in the in memory h2 database with the base class:
package com.example.demo.user;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;
import java.util.Date;
#Entity
public class User {
#Id
#GeneratedValue
private Integer id;
#Size(min=2,message = "Name length >= 2")
private String name;
#Past
private Date dob;
public User(Integer id, String name, Date dob) {
this.id = id;
this.name = name;
this.dob = dob;
}
#Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", dob=" + dob +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getDob() {
return dob;
}
public void setDob(Date dob) {
this.dob = dob;
}
}
data.sql - insert into user values(1,sysdate(),'h');
schema.sql - create table user (id integer not null, dob timestamp, name varchar(255), primary key (id))
application.properties:
spring.jpa.show-sql=true
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.data.jpa.repositories.bootstrap-mode=default
unable to see the data. Initialising through commandlinerunner worked. The log does not show the command being executed. but if i try removing schema.sql, throws error.
Spring will use those scripts schema.sql and data.sql and after that it will also use your entity layer and annotations to create-drop the schema again, meaning the previous changes with the scripts are overwritten.
Since you provide your own schema.sql , it means that you don't need the ORM vendor to create the schema for you based on the entity annotations that you have.
In this case you can procced and declare the property spring.jpa.hibernate.ddl-auto= none
This will not allow the ORM vendor to overwrite what you do with your scripts and only those scripts will be used when loading the application.
PS: spring.jpa.hibernate.ddl-auto= create-drop was already by default defined for you from spring automatically as you have an embedded database selected.
I have a Spring Boot User class which always comes up with the error "java.sql.SQLException: Field 'id' doesn't have a default value". I have tried many times to provide a default value, both in the Java class and in the database table, but to no avail. And I have also switched from generation type = auto and = identity, but to no avail. Thank you very much for your help. Here is my Java Class and my Database Table:
package com.ykirby.myfbapp;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.springframework.beans.factory.annotation.Value;
import java.sql.Timestamp;
import java.util.Date;
#Entity // This tells Hibernate to make a table out of this class
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
#Value("#{User.id ?: 0}")
private int id = 12345;
#Column(name = "fbuserid")
private String fbuserid;
#Column(name = "apttime")
#Temporal(TemporalType.TIMESTAMP)
private Date apttime;
#Column(name = "apttitle")
private String apttitle;
#Column(name = "aptaddress")
private String aptaddress;
#Column(name = "aptlonglat")
private String aptlonglat;
#Column(name = "aptdetails")
private String aptdetails;
public String getFbuserid() {
return fbuserid;
}
public void setFbuserid(String fbuserid) {
this.fbuserid = fbuserid;
}
public Date getApttime() {
return apttime;
}
public void setApttime(Date apttime) {
this.apttime = apttime;
}
public String getApttitle() {
return apttitle;
}
public void setApttitle(String apttitle) {
this.apttitle = apttitle;
}
public String getAptaddress() {
return aptaddress;
}
public void setAptaddress(String aptaddress) {
this.aptaddress = aptaddress;
}
public String getAptlonglat() {
return aptlonglat;
}
public void setAptlonglat(String aptlonglat) {
this.aptlonglat = aptlonglat;
}
public String getAptdetails() {
return aptdetails;
}
public void setAptdetails(String aptdetails) {
this.aptdetails = aptdetails;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
The changes in your User entity class may sometimes not reflect your DB schema accurately. You can try one of these below solutions:
1. Update your DB schema manually by adding AUTO_INCREMENT attribute
ALTER TABLE `user` CHANGE COLUMN `id` `id` INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT;
2. Drop the User table in your DB, and rerun the application
Make sure that spring.jpa.hibernate.ddl-auto is set to create or update. The default is none if you are NOT using an embedded/in-memory DBs like H2 database.
This configuration of Spring Data JPA will set Hibernate's hibernate.hbm2ddl.auto to the setting value. In our case, it is create or update.
You can read more about this in the below articles and docs.
Spring Boot reference - Database Initialization
What are the possible values of the Hibernate hbm2ddl.auto configuration and what do they do
In production, I suggest you not to use this option but instead use a database migration tool like Liquibase or Flyway and leave the spring.jpa.hibernate.ddl-auto configuration to be none.
More reads about this Hibernate: hbm2ddl.auto=update in production?
You should drop the existing database and re-generate it because sometimes changes done through the model don't reflect properly in the database. While re-generating the database you can scaffolding it with SchemaExport.
Does
#Column(name = "apttitle")
private String apttitle="default";
work?
few days back i starting learning hibernate JPA but i am unable to find solution of given problem below
My Project consist three class employee ,phone ,department by seeing code you can easily understand what i am doing .
Main problem raise when i try to save this data into database using spring boot controller it showing null value in column . In employee table department_id is null(not automatic update using cascade.All) same in phone table employee_id is null.
I do not want update manually .is their any way so dep_id and emp_id automatic update to foreign key table .
{
"name":"CSE",
"employees":[
{
"name":"Welcome",
"age":23,
"phones":[{"number":1234567890},{"number":1234567890}]
},
{
"name":"back",
"age":25,
"phones":[{"number":1234567890},{"number":1234567890}]
}
]
}
package com.example.entity;
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name = "employee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private int age;
#ManyToOne
#JoinColumn(name = "department_id")
private Department department;
#OneToMany(mappedBy = "employee", cascade=CascadeType.ALL)
private List<Phone> phones;
// getters and setters...
}
package com.example.entity;
import javax.persistence.*
#Entity
public class Phone {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String number;
#ManyToOne
#JoinColumn(name = "employee_id")
private Employee employee;
// getters and setters...
}
package com.example.entity;
import javax.persistence.*;
import java.util.List;
#Entity
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
#OneToMany(mappedBy = "department",cascade = CascadeType.ALL)
private List<Employee> employees;
}
dept table in database
id name
1 CSE
employee table
id name age department_id
1. welcome 23. null
2. back. 25. null
phone table
id number employee_id
1. 1234567890. null
2. 1234567890. null
3. 1234567890 null
4. 1234567890. null
Why employee_id and department_id not updating automatic in cascade All
Controller class
package com.example.controller;
import com.example.dao.DepRepo;
import com.example.dao.EmployeeRepo;
import com.example.dao.PhoneRepo;
import com.example.entity.Department;
import com.example.entity.Employee;
import com.example.service.FakeService;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import jakarta.inject.Inject;
#Controller("/dummy")
public class DummyController {
#Inject
FakeService fakeService;
#Inject
PhoneRepo phoneRepo;
#Inject
EmployeeRepo employeeRepo;
#Inject
DepRepo depRepo;
#Get ("/")
public String fun(){
fakeService.fun();
return "welcome back";
}
#Post("/add")
public HttpResponse<?> fun(#Body Department dep){
System.out.println(dep);
depRepo.save(dep);
return HttpResponse.status(HttpStatus.ACCEPTED).body("data add successfully");
}
}
Your Hibernate mapping says that the relationships are mapped by the 'many' side of the association:
#OneToMany(mappedBy = "department",cascade = CascadeType.ALL)
private List<Employee> employees;
So Hibernate looks for the value of the 'department' in the employee entity and it is null (because there's no value in the JSON data)
So try removing mappedBy to tell Hibernate that the relationship is mapped on the 'one' side
I have added a foreign key as such:
#ManyToOne
#JoinColumn
private UserEntity userEntity;
And my table has these columns:
CREATE TABLE `web_course` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`coursename` varchar(255) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
`user_entity_user_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK6eys4s4qx87rxo0ha68q05oc8` (`user_entity_user_id`),
CONSTRAINT `FK6eys4s4qx87rxo0ha68q05oc8` FOREIGN KEY (`user_entity_user_id`) REFERENCES `web_user` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
I would like to create a method to findByUserId in:
#Repository
public interface CourseRepository extends JpaRepository<CourseEntity, Long> {
I have tried several combinations in vain:
public CourseEntity findByUserId(int user_id)
public CourseEntity findByUserEntityUserId(int user_id)
public CourseEntity findBy_User_entity_user_id(int user_id)
I read that there is a naming convention but I can't seem to find the correct convention since I am getting:
Error creating bean with name 'courseController': Unsatisfied dependency expressed through field 'courseRepository';
Course class:
package com.finaly.projectback.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#Entity
#Table(name = "web_course")
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class CourseEntity {
#ManyToOne
#JoinColumn
private UserEntity userEntity;
public UserEntity getUserEntity() {
return userEntity;
}
public void setUserEntity(UserEntity userEntity) {
this.userEntity = userEntity;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", updatable = false, nullable = false)
private long id;
#Column(name = "coursename")
private String coursename;
#Column(name = "description")
private String description;
public CourseEntity() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getCoursename() {
return coursename;
}
public void setCoursename(String coursename) {
this.coursename = coursename;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public CourseEntity(UserEntity userEntity, long id, String coursename, String description) {
super();
this.userEntity = userEntity;
this.id = id;
this.coursename = coursename;
this.description = description;
}
#Override
public String toString() {
return "CourseEntity [userEntity=" + userEntity + ", id=" + id + ", coursename=" + coursename + ", description="
+ description + "]";
}
}
Can someone point me in the right direction please?
Following code should work:
public CourseEntity findByUserEntity_UserId(int user_id)
Check logs to see if the course repository is instantiated or not. Error indicates controller can'be created because of the dependency issue.
Error creating bean with name courseController: Unsatisfied dependency expressed through field courseRepository
Hibernate is not generating a table for the dataAttributes Map in the MetaData class below. The code compiles but table not found at runtime.
import javax.persistence.*;
import java.util.HashMap;
import java.util.Map;
#Entity
public class Metadata{
private Integer id;
private Map<String,String> dataAttributes;
public Metadata(){
dataAttributes = new HashMap<>();
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public void addDataAttribute(String key, String value){
dataAttributes.put(key,value);
}
#ElementCollection
#MapKeyColumn(name="key")
#Column(name="value")
#CollectionTable(name="data_attributes", joinColumns=#JoinColumn(name="metaData_id"))
public Map<String, String> getDataAttributes() {
return dataAttributes;
}
public void setDataAttributes(Map<String, String> dataAttributes) {
this.dataAttributes = dataAttributes;
}
}
All the other entities and tables are created as expected but this one is never generated and I get "Table 'nppcvis.data_attributes' doesn't exist" when trying to save an entity that has a one-to-one relationship with MetaData with cascade=all
I'm using the following property :
spring.jpa.hibernate.ddl-auto=create
Any ideas?
Removing all annotations aside from #ElementCollection result in table being created. Obviously no control over naming but it works.
Not allowed to assign name in #MapKeyColumn as key but you can wrap in \" like here
"\"key\""
In your case:
#ElementCollection
#MapKeyColumn(name="\"key\"")
#Column(name="value")
#CollectionTable(name="data_attributes", joinColumns=#JoinColumn(name="metaData_id"))
public Map<String, String> getDataAttributes() {
return dataAttributes;
}