How do I specify a default value for an identity variable in Spring Boot? - spring

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?

Related

Unable to see data in in memor h2 data base

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.

EntityManager Configuration to Fetch Data Oracle DB - SpringBoot [duplicate]

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.

JPA repository method returning no data even though data is present in H2 db

ExchangeValue exchangeValue = repository.findByFromAndTo(from, to);
The exchangeValue is coming as null though data is present in h2 db
H2 data snapshot
My code url is https://github.com/sunny107842/currency-exchange
package com.sunny.microservices.currencyexchangeservice;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ExchangeValueRepository extends
JpaRepository<ExchangeValue, Long>{
ExchangeValue findByFromAndTo(String from, String to);
}
Edit
Entire code can be found at the github url
Exchange class
`
package com.sunny.microservices.currencyexchangeservice;
import java.math.BigDecimal;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class ExchangeValue {
#Id
private Long id;
#Column(name = "currency_from")
private String from;
#Column(name = "currency_to")
private String to;
private BigDecimal conversionMultiple;
private int port;
public ExchangeValue() {
}
public ExchangeValue(Long id, String from, String to, BigDecimal conversionMultiple) {
super();
this.id = id;
this.from = from;
this.to = to;
this.conversionMultiple = conversionMultiple;
}
public Long getId() {
return id;
}
public String getFrom() {
return from;
}
public String getTo() {
return to;
}
public BigDecimal getConversionMultiple() {
return conversionMultiple;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}
Could anyone help me out please? Data is present in the db
i am making this rest call to get the data
http://localhost:8001/currency-exchange/from/usd/to/inr
Please let me know if any other data is required.
It's very simple. In your data.sql you inserted values with uppercase. Try with http://localhost:8001/currency-exchange/from/USD/to/INR or change it in data.sql to lowercase.
Hi Issue was in the rest call.It was the error of case sensitive
Right call should be
http://localhost:8001/currency-exchange/from/USD/to/INR
instead of
http://localhost:8001/currency-exchange/from/usd/to/inr
It was the case sensitive Issue:
Change usd to USD, ind to INR
Call
GET: http://localhost:8001/currency-exchange/from/USD/to/INR

Hibernate not creating ElementCollection table

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;
}

SpringMVC+Hibernate : criteria.list() is returning an empty list

I am using spring MVC with Hibernate, The aim is to get the table data and store it in a list.Here the entity class being used :
package com.bng.core.entity;
// default package
// Generated Oct 25, 2015 4:38:03 PM by Hibernate Tools 3.4.0.CR1
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* servicenames generated by hbm2java
*/
#Entity
#Table(name = "servicenames")
public class ServiceNames implements java.io.Serializable {
private Integer id;
private String serviceName;
public ServiceNames() {
}
public ServiceNames(String servicename) {
this.serviceName = servicename;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
#Column(name = "servicename", length = 25)
public String getServiceName() {
return this.serviceName;
}
public void setServiceName(String servicename) {
this.serviceName = servicename;
}
}
And the method used to get the list :
#Transactional
#Override
public List<ServiceNames> getServiceNames() {
Logger.sysLog(LogValues.APP_INFO, this.getClass().getName(), "Getting all Service names.");
Session session = sessionFactoryGlobal.openSession();
Criteria criteria = session.createCriteria(ServiceNames.class);
List<ServiceNames> serviceNamesList = criteria.list();
session.close();
return serviceNamesList;
}
When the method is called it returns an empty list. Please suggest where its going wrong ?
I think you are sure your table servicenames has data. So such problem can be when #Transactional is not working properly. Try to get list without #Transactional by open and close a transaction manually.

Resources