PSQLException: ERROR: null value in column "person" violates not-null constraint when trying class-based projection - spring-boot

I cannot solve my problem, although following a documentation regarding class-based projection.
I always get the following error message:
PSQLException: ERROR: null value in column "person" violates not-null constraint
This is my entity class
#Entity
#Table(name="incomeoutgo", schema = "public")
public class IncomeOutgo extends AbstractPersistable<Long> {
#NotNull
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name ="id")
private Long id;
#NotNull
#Column(name="dayofweek")
private Date dayofweek;
#NotNull
#Size(min= 2, max= 100)
#Column(name="position")
private String position;
#NotNull
#Size(min = 5, max = 50)
#Column(name ="person")
private String person;
#NotNull
#Size(min= 1)
#Column(name="income")
private double income;
#NotNull
#Size(min= 2)
#Column(name="outgo")
private double outgo;
}
So does my Repository look like:
#Repository
public interface ChooseMonthRepository extends JpaRepository<IncomeOutgo, Date> {
#Query(value = "SELECT dayofweek, person, position, income, outgo FROM IncomeOutgo WHERE dayofweek >= :start_dayofmonth AND dayofweek <= :end_dayofmonth", nativeQuery = true)
List<DateChoiceDTO> findAllByDate(#Param("start_dayofmonth") Date start_dayofmonth, #Param("end_dayofmonth") Date end_dayofmonth);
}
This is my class-based projection
#AllArgsConstructor
#NoArgsConstructor
#Data
public class DateChoiceDTO {
Date dayofweek;
String person;
String position;
double income;
double outgo;
}
The Service class
#RequiredArgsConstructor
#Service
#Transactional(readOnly=true)
public class DateChoiceService {
private final ChooseMonthRepository incomeOutgoDateChoice;
public List<?> getAllForDateChoice(Date start_dayofmonth) {
Date lastDayOfMonth=java.util.Calendar.getInstance().getTime();
return incomeOutgoDateChoice.findAllByDate(start_dayofmonth, lastDayOfMonth);
}
}
And last but not least my Controller
#RequiredArgsConstructor
#Controller
#Validated
#RequestMapping(value = "/")
public class DateChoiceController {
private static final String DATE_CHOICE_VIEW = "DateChoice";
private final DateChoiceService dateChoiceService;
#GetMapping("/")
public String homeInit(Model model) {
return DATE_CHOICE_VIEW;
}
#PostMapping(value = "/")
public String addUser(#ModelAttribute("incomeoutgo") #Valid DateChoiceDTO dateChoice, Model model, #NotNull BindingResult bindingResult) {
if(bindingResult.hasErrors()) {
return DATE_CHOICE_VIEW;
}
List<?> incomeOutgoList = dateChoiceService.getAllForDateChoice(dateChoice.getDayofweek());
model.addAttribute(DATE_CHOICE_VIEW, incomeOutgoList);
return DATE_CHOICE_VIEW;
}
}
I do not understand why the person column suddenly comes into play?
Maybe someone can tell me what I am doing wrong here?

You are getting following exception because the framework is not sure what to do with the tuple.
No converter found capable of converting from type AbstractJpaQuery$TupleConverter$TupleBackedMap to type DateChoiceDTO
You have to use fully qualified name of the class when you are using construction result set mapping in the query like below:
select new a.b.cSomeDto(t.property1, t.property2) from table t
Here is how i have fixed it:
#Query(value = "SELECT new com.example.samplejdbctemplatecall.DateChoiceDTO" +
"(table.dayofweek, table.person, table.position, table.income, table.outgo) " +
"FROM IncomeOutgo table WHERE table.dayofweek >= :start_dayofmonth AND table.dayofweek <= :end_dayofmonth")
List<DateChoiceDTO> findAllByDateSecond(#Param("start_dayofmonth") Date start_dayofmonth, #Param("end_dayofmonth") Date end_dayofmonth);
But as mentioned by Simon, if you have insert query firing from somewhere else you need to fix that first, because this is a complete retrieval operation and the error you are facing is related to data insertion.
Entire working sample(+ interface projection):
#RequestMapping("/test")
#RestController
class IncomeOutgoController {
private final ChooseMonthRepository chooseMonthRepository;
private int days = 0;
#Autowired
IncomeOutgoController(ChooseMonthRepository chooseMonthRepository) {
this.chooseMonthRepository = chooseMonthRepository;
}
#GetMapping("/interface-projection")
public Object interfaceProjection() {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MONTH, -1);
Date date = calendar.getTime();
calendar = Calendar.getInstance();
return composeResponse("interface-projection", chooseMonthRepository.findAllByDate(date, calendar.getTime()));
}
#GetMapping("/constructor-projection")
public Object constructorProjection() {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MONTH, -1);
Date date = calendar.getTime();
calendar = Calendar.getInstance();
return composeResponse("constructor-projection", chooseMonthRepository.findAllByDateSecond(date, calendar.getTime()));
}
#GetMapping("/store")
public Object addRandomData() {
String randomUuid = UUID.randomUUID().toString();
chooseMonthRepository.save(new IncomeOutgo(null, Calendar.getInstance().getTime(), "random-position: " + randomUuid,
randomUuid, new Random(5000).nextDouble(), new Random(500).nextDouble()));
return "success";
}
private Map<String, Object> composeResponse(String key, Object value) {
final Map<String, Object> response = new HashMap<>();
response.put(key, value);
return response;
}
}
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "incomeoutgo", schema = "public")
class IncomeOutgo extends AbstractPersistable<Long> {
#NotNull
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#NotNull
#Column(name = "dayofweek")
private Date dayofweek;
#NotNull
#Size(min = 2, max = 100)
#Column(name = "position")
private String position;
#NotNull
#Size(min = 5, max = 50)
#Column(name = "person")
private String person;
#NotNull
#Size(min = 1)
#Column(name = "income")
private double income;
#NotNull
#Size(min = 2)
#Column(name = "outgo")
private double outgo;
}
#Repository
interface ChooseMonthRepository extends JpaRepository<IncomeOutgo, Date> {
#Query(value = "SELECT dayofweek, person, position, income, outgo FROM IncomeOutgo WHERE dayofweek >= :start_dayofmonth AND dayofweek <= :end_dayofmonth", nativeQuery = true)
List<DateChoiceDTOInterface> findAllByDate(#Param("start_dayofmonth") Date start_dayofmonth, #Param("end_dayofmonth") Date end_dayofmonth);
#Query(value = "SELECT new com.example.samplejdbctemplatecall.DateChoiceDTO" +
"(table.dayofweek, table.person, table.position, table.income, table.outgo) " +
"FROM IncomeOutgo table WHERE table.dayofweek >= :start_dayofmonth AND table.dayofweek <= :end_dayofmonth")
List<DateChoiceDTO> findAllByDateSecond(#Param("start_dayofmonth") Date start_dayofmonth, #Param("end_dayofmonth") Date end_dayofmonth);
}
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
class DateChoiceDTO {
Date dayofweek;
String person;
String position;
Double income;
Double outgo;
}
interface DateChoiceDTOInterface {
Date getDayOfWeek();
String getPerson();
String getPosition();
Double getIncome();
Double getOutgo();
}
application.properties
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:postgresql://172.17.0.2:5432/postgres
spring.datasource.username=postgres
spring.datasource.password=mysecretpassword
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.show-sql=true

Related

How to create Search Functionality using Optional Parameter in Spring Boot

I want to build a search functionality in my application. There are many parameters which can be optional.
Model
#Getter
#Setter
#EqualsAndHashCode(exclude = "inventoryInspectionReport")
#NoArgsConstructor
#Entity()
public class Inventory {
#SequenceGenerator(
name = "car_inventory_sequence",
sequenceName = "car_inventory_sequence",
allocationSize = 1
)
#Id
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "car_inventory_sequence"
)
private Long id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String owner;
#Column(nullable = false)
private String km_driven;
#Column(nullable = false)
private Integer price;
#Column
private String fuelType;
#Column
private String location;
#Column
private String insuranceValidity;
#Column
private String rto;
}
Controller
#GetMapping("/buy-car")
public String showBuyCarPageList(Model model,
#RequestParam("page") Optional<Integer> page,
#RequestParam("size") Optional<Integer> size
){
int currentPage = page.orElse(1);
int pageSize = size.orElse(16);
Page<Inventory> pageObj = carInventoryService.listAllCarIfShow(currentPage, pageSize);
int totalPages = pageObj.getTotalPages();
long totalItems = pageObj.getTotalElements();
List<Inventory> carInventoryList = pageObj.getContent();
model.addAttribute("currentPage", 1);
model.addAttribute("totalPages", totalPages);
model.addAttribute("totalItems", totalItems);
model.addAttribute("carInfo", carInventoryList);
return "frontend/buy-car-list";
}
#Service
#AllArgsConstructor
public class InventoryServiceImpl implements InventoryService {
#Autowired
private Environment env;
InventoryRepository carInventoryRepository;
InventoryImagesRepository carInventoryImagesRepository;
#Override
public Page<Inventory> listAllCarIfShow(int pageNumber, int pageSize) {
Pageable pageable = PageRequest.of(pageNumber - 1, pageSize);
return carInventoryRepository.findAllByShow(true, pageable);
}
}
My question is, how i can create a search functionality? There can be some parameter null? How i can query or ignore the parameters?
Bellow is query samples
http://localhost:8080/buy-car?page=1&size=1&name=Car2
http://localhost:8080/buy-car?page=1&size=1&name=Car2&owner=1st
http://localhost:8080/buy-car?page=1&size=1&fuelType=Petrol&owner=1st
Sample form image
Assuming that you are using JPA. You can handle it by writing a query as below. This will ignore the where condition if the parameter is null.
#Repository
public class InventoryRepository extends JPARepository<Inventory, Long> {
#Query("SELECT i FROM Inventory i WHERE (:name is null or i.name = :name) and (:owner is null or i.owner = :owner)")
Page<Inventory> findAllByShow (String name, String owner, Pageable pageable);
}
PS: You need to update your Controller and Service layers to accept other parameters such as name, owner etc..

JPA Failing to produce a proper SQL query when a parameter has a composite primary key

Today I came across a weird bug while trying to test a JPA update query and I'm wondering if this a SpringBoot bug.
I have the following entities
An Entry entity
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor(access = AccessLevel.PROTECTED, force = true)
public class Entry {
#Id
private String id;
#ManyToOne(targetEntity = User.class)
#JoinColumn(referencedColumnName = "username")
#NotNull
private final User username;
#Enumerated(EnumType.STRING)
#NotNull
private Type type;
#ManyToOne(targetEntity = Category.class)
#JoinColumns({#JoinColumn(referencedColumnName = "name"),#JoinColumn(referencedColumnName = "type"),#JoinColumn(referencedColumnName = "username")})
#NotNull
private Category category;
#Size(max = 45)
#NotBlank
private String description;
#NotNull
private Double amount;
#NotNull
private final Date createdAt;
private Timestamp lastUpdate;
#NotNull
private Boolean isDeleted;
public enum Type{
Income,Expense
}
}
A Category entity with a composite key
#Entity
#NoArgsConstructor(access = AccessLevel.PROTECTED, force = true)
#Setter
#Getter
#EqualsAndHashCode(of = {"id"})
#ToString(of = {"id"})
public class Category {
#EmbeddedId
private CategoryId id;
private final Timestamp createdAt = Timestamp.from(Instant.now());
#ManyToOne(targetEntity = User.class)
#JoinColumn(referencedColumnName = "username")
private final User user;
#OneToMany(cascade = CascadeType.ALL,mappedBy = "category")
private List<Entry> entries;
public Category(String name, Type type, User user){
this.id = new CategoryId(name,type,user.getUsername());
this.user = user;
}
}
A CategoryID that is the embeddable composite key of the Category entity
#Data
#Embeddable
#AllArgsConstructor
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#EqualsAndHashCode(of = {"name","type","username"})
public class CategoryId implements Serializable {
private String name;
#Enumerated(EnumType.STRING)
private Type type;
private String username;
}
The following repository
#Repository
public interface EntryRepository extends JpaRepository<Entry, String> {
Optional<Entry> findEntryById(String id);
#Modifying(clearAutomatically = true, flushAutomatically = true)
#Query(value = "UPDATE Entry e SET e.username = :username, e.type = :type, e.category = :category, e.description = :description, e.amount = :amount, e.createdAt = :date, e.lastUpdate = :lastUpdate, e.isDeleted = :isDeleted WHERE e.id = :id")
void update(#Param("id") String id,
#Param("username") User username,
#Param("type") Entry.Type type,
#Param("category") Category category,
#Param("description") String description,
#Param("amount") Double amount,
#Param("date") Date date,
#Param("lastUpdate") Timestamp lastUpdate,
#Param("isDeleted") Boolean isDeleted);
}
And finally the following Unit Test
#Test
void update() {
//given
User testUser = userRepository.save(new User("testUser#test.com","000000000000000000000000000000000000000000000000000000000000"));
Category testCategory = categoryRepository.save(new Category("Test Category", Entry.Type.Income,testUser));
Entry testEntry = new Entry("testEntry",testUser, Entry.Type.Income,
testCategory, "test",
0.0, new Date(343), from(now()), false);
System.out.println(testCategory);
entryRepositoryUnderTest.save(testEntry);
//when
entryRepositoryUnderTest.update("testEntry",testUser,Expense,testCategory,"testUpdated",1.0,new Date(346), from(now()),true);
Optional<Entry> actual = entryRepositoryUnderTest.findEntryById("testEntry");
System.out.println(actual.get().getCategory());
//then
assertThat(actual.get().getUsername()).isEqualTo(testUser);
assertThat(actual.get().getType()).isEqualTo(Expense);
assertThat(actual.get().getCategory()).isEqualTo(testCategory);
assertThat(actual.get().getDescription()).isEqualTo("testUpdated");
assertThat(actual.get().getAmount()).isEqualTo(1.0);
assertThat(actual.get().getIsDeleted()).isEqualTo(true);
}
When I run the test it fails and I get the following error message:
could not execute update query; SQL [update entry set username_username=?, type=?,category_name=?=category_type=?, description=?, amount=?, created_at=?, last_update=?, is_deleted=? where id=?]; nested exception is org.hibernate.exception.DataException: could not execute update query
As you can see here when SpringBoot is trying to produce a SQL query statement from my #Query parameter it can not properly extract the Category field from the parameters and inject it's composite embeddable key into the SQL statement. It has no problem extracting the User parameter because the User is an entity with an id that is not composite.
Is this a SpringBoot bug or am I missing something?
EDIT:
This is the structure of the database

Spring Hibernate - Fetch column value using #Formula from max value based of other table

I have two tables with one to many relation. Lead and Leadactivity. A lead can have multiple activities.
Problem Statement -
I want an additional column in lead table to know the last modified date of any lead. Last modified date will be the date when last activity was created or updated. So, I am using #Formula to fetch the column. However, I am not able to get the correct date instead I am getting null value for field lastModifiedDate. Can anyone help where I am going wrong. Below are the table structure
#Entity
#Table(name = "customer_lead")
#Where(clause = ReusableFields.SOFT_DELETED_CLAUSE)
#Audited(withModifiedFlag = true)
#Data
public class Lead extends ReusableFields implements Serializable
{
//other fields
#NotAudited
#Formula("(Select max(modified) from lead_activity la Where la.lead_id=lead_id)")
Date lastModifiedDate;
}
Lead Activity
#Entity
#Table(name = "LeadActivity")
#Data
#Where(clause = ReusableFields.SOFT_DELETED_CLAUSE)
public class LeadActivity extends ReusableFields implements Serializable
{
// other fields
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "lead_id", nullable = false)
#JsonIgnoreProperties(
{ "hibernateLazyInitializer", "handler" })
#NotFound(action = NotFoundAction.IGNORE)
Lead lead;
}
Mapped super class for modified field
#MappedSuperclass
#Audited
public class ReusableFields implements Serializable
{
public static final String SOFT_DELETED_CLAUSE = "is_deleted = 'false'";
#Column(name="is_deleted", columnDefinition="BOOLEAN DEFAULT true")
public boolean isDeleted;
#CreationTimestamp
#Column(name = "created_at")
#JsonProperty("created")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss ")
private Date created;
#Column(name = "updated_at")
#JsonProperty("updated")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
#UpdateTimestamp
private Date modified;
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public Date getModified() {
return modified;
}
public void setModified(Date modified) {
this.modified = modified;
}
public boolean isDeleted() {
return isDeleted;
}
public void setDeleted(boolean isDeleted) {
this.isDeleted = isDeleted;
}
public static String getSoftDeletedClause() {
return SOFT_DELETED_CLAUSE;
}
}
Handled this by adding below formula
#NotAudited
#Formula("(Select max(la.updated_at) from Lead_Activity la Where la.lead_id=lead_id)")
Date lastModifiedDate;

JPA repository null pointer exception for many to one mapping with composite primary key

Post class
one to many mapping
Composite primary key using id
I am getting null pointer exception when I make get request for getting comments
#Entity
#Table(name = "posts")
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Size(max = 100)
#Column(unique = true)
private String title;
#NotNull
#Size(max = 250)
private String description;
#NotNull
#Lob
private String content;
#NotNull
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "posted_at")
private Date postedAt = new Date();
#NotNull
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "last_updated_at")
private Date lastUpdatedAt = new Date();
#OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "post")
private Set<Comment> comments = new HashSet<>();
public Post() {
}
public Post(String title, String description, String content) {
this.title = title;
this.description = description;
this.content = content;
}
//getters and setters
}
Comment class
many to one mapping with composite primary keys using #Idclass
#Entity
#IdClass(CommentId.class)
#Table(name = "comments")
public class Comment {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Lob
private String text;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "post_id", nullable = false)
private Post post;
public Comment() {
}
public Comment(String text) {
this.text = text;
}
//getters and setters
}
Id class
CommentId
public class CommentId implements Serializable {
private static final long serialVersionUID = 1L;
private Post post;
private Long id;
public CommentId(Post post, Long id) {
super();
this.post = post;
this.id = id;
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result+ ((post == null) ? 0 : post.hashCode());
result = prime * result ;
return result;
}
public boolean equals(Object object) {
if (object instanceof CommentId) {
CommentId pk = (CommentId)object;
return id.equals(pk.id) && post == pk.post;
} else {
return false;
}
}
//getters and setters
}
repositories
PostRepository
CommentRepository
#Repository
public interface PostRepository extends JpaRepository<Post, Long> {
}
#Repository
public interface CommentRepository extends JpaRepository<Comment, Long>
{
}
Controller class get request and I am using mysql database
#RestController
#RequestMapping("/demo")
public class Controller {
#Autowired
PostRepository ps;
CommentRepository cs;
#GetMapping("/post")
public List<Post> getAll(){
return ps.findAll();
}
#GetMapping("/comment")
public List<Comment> getAllcom(){
return cs.findAll();
}
}

Spring Data Rest with Jpa relations

Followed this question but did not work
Have two entities Account and UserTransaction
Account.java
#Entity
#Access(AccessType.FIELD)
public class Account {
#Id
private Integer accountNumber;
private String holderName;
private String mobileNumber;
private Double balanceInformation;
public Account(Integer accountNumber, String holderName, String mobileNumber, Double balanceInformation) {
this.accountNumber = accountNumber;
this.holderName = holderName;
this.mobileNumber = mobileNumber;
this.balanceInformation = balanceInformation;
}
}
UserTransaction.java
#Entity
#Access(AccessType.FIELD)
#Table(name = "user_transaction")
public class Transaction {
#Id
private Long transactionId;
#ManyToOne
#JoinColumn(name = "accountNumber")
private Account accountNumber;
private Double transactionAmount;
#Column(nullable = false, columnDefinition = "TINYINT", length = 1)
private Boolean transactionStatus;
private String statusMessage;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="timestamp", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
private Date timestamp;
public Transaction(Long transactionId, Account account,
Double transactionAmount,
Boolean transactionStatus,
String statusMessage) {
this.transactionId = transactionId;
this.accountNumber = account;
this.transactionAmount = transactionAmount;
this.transactionStatus = transactionStatus;
this.statusMessage = statusMessage;
}
}
and My TransactionRepository is as follows
#RepositoryRestResource(collectionResourceRel = "transactions", path = "transactions")
public interface JpaTransactionRepository extends JpaRepository<Transaction, Long>, TransactionRepository {
#Query(value = "select t from Transaction t where t.accountNumber.accountNumber = :accountNumber")
Iterable<Transaction> findByAccountNumber(#Param("accountNumber") Integer accountNumber);
}
I have constructed a json as specified in the stackoverflow post at the top
{
"transactionId" : "3213435454342",
"transactionAmount" : 5.99,
"transactionStatus" : true,
"statusMessage" : null,
"timestamp" : "2017-03-09T05:11:41.000+0000",
"accountNumber" : "http://localhost:8080/accounts/90188977"
}
when I try to execute POST with the above json I get
Caused by: java.sql.SQLIntegrityConstraintViolationException: Column 'account_number' cannot be null
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:533)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:513)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:115)
at com.mysql.cj.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:1983)
How do I save an entity that has relationships with Spring data rest????
The problem is that with #JoinColumn(name = "accountNumber") you would hard-code the column name in database as accountNumber. Normally the naming-strategy would add embedded underscores instead of having mixed case column names.
So it should work if you change the line to #JoinColumn(name = "account_number").

Resources