Change Spring Data Rest default message for DataIntegrityViolationException - spring-data-rest

I have a basic JPA Entity that I would like to expose via Spring-Data-Rest
#Entity
#Table(name='test')
class Test{
#Id
Long id
#Column(name='other_id', unique = true)
String otherId
#Column(name='other_crm_id')
String otherCrmId
#Column(name='created_date')
Date createdDate
#Column(name='created_by')
String createdBy
}
I would like to change the exception message that is thrown when the Unique constraint on 'otherId' fires. Currently the default ExceptionHandler for Spring Data Rest displays the following
{
-cause: {
-cause: {
cause: null
message: "Unique index or primary key violation:
\"UK_PJCWVB8DO3C89YTD1PNF85HQR_INDEX_2 ON PUBLIC.TEST(OTHER_ID) VALUES
( /* key:1 */ 2, NULL, NULL, 'ABC123424', '123')\"; SQL statement:\
insert into test (id, created_by, created_date, other_crm_id,
other_id) values (null, ?, ?, ?, ?) [23505-175]"
}
message: "could not execute statement"
}
message: "could not execute statement; SQL [n/a]; constraint
[\"UK_PJCWVB8DO3C89YTD1PNF85HQR_INDEX_2 ON PUBLIC.TEST(OTHER_ID) VALUES ( /* key:1
*/ 2, NULL, NULL, 'ABC123424', '123')\"; SQL statement:\ insert into test (id,
created_by, created_date, other_crm_id, other_id) values (null, ?, ?, ?, ?)
[23505-175]]; nested exception is
org.hibernate.exception.ConstraintViolationException: could not execute statement"
}
Not a big fan of the sql and table information being returned in the Response, so I've been attempting to change the message. I created a ExceptionHandlingController annotated with #ControlAdvice, but the ExceptionHandlers in AbstractRepositoryRestController.java take precedence.
My question is: What is the best way to change the error response for the DataIntegrityViolationException in Spring-Data-Rests default RepositoryEntityController?

Related

MariaDB Node.js-Connector insert a value and use the default value

I have a MariaDB database in conjuction with an Express.js backend and therefore use the MariaDB provided Node.js Connector. I Initialized a Database with a Table that looks like this:
CREATE TABLE IF NOT EXISTS `Threads` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`title` TINYTEXT NOT NULL,
`post` TEXT NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (`id`)
);
Using the Node.js-Connector in my Backend, I want to insert some data to this table and use the default values for "created_at" and "updated_at".
I was thinking of something like this:
const insertThreadQuery = 'INSERT INTO Threads VALUES (?, ?, ?, ?, ?)';
con = await pool.getConnection();
const result = await con.query(insertThreadQuery, [null, "title", "post", null, null]);
Which obivously throws an error that tells me, that I cannot insert null for these Values ((conn=4, no: 1048, SQLState: 23000) Column 'updated_at' cannot be null sql: INSERT INTO Threads VALUES (?, ?, ?, ?, ?) - parameters:[null,'title','post',null,null])
My question is: How can I insert an entry like I was showing before, but instead of inserting "null" insert something else so my columns created_at and updated_at use the default value?
If you want to use DEFAULT values, then pass DEFAULT instead of NULL.
const insertThreadQuery = 'INSERT INTO Threads VALUES (NULL, ?, ?, DEFAULT, DEFAULT)';
con = await pool.getConnection();
const result = await con.query(insertThreadQuery, ["title", "post"]);

Nodejs Mysql Insert is adding backslashes to strings and fails

I'm trying to do a simple sql insert using nodejs and express trying various formats I've found online, but this appears to be the accepted way. When I view the error it's adding extra backslashes to the query and failing.
The code:
console.log(request.body);
var post = {uid: request.session.uid, title: request.body.title, created: request.body.createdAt};
connection.query('INSERT INTO projects (uid, title, created) SET ? ', post, function (error, results, fields) {
console.log(error);
});
The first body console.log:
{ title: 'aewgawegr', createdAt: '1574219119301' }
The error message:
sqlMessage:
'You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near \'SET `uid` = 1, `title` = \'aewgawegr\', `created` = \'1574219119301\'\' at line 1',
sqlState: '42000',
index: 0,
sql:
'INSERT INTO projects (uid, title, created) SET `uid` = 1, `title` = \'aewgawegr\', `created` = \'1574219119301\' ' }
For reference: https://dev.mysql.com/doc/refman/8.0/en/insert.html
You cannot combine the two usage syntax:
INSERt INTO `table` (column1, ...) VALUES (value1, ...
with
INSERT INTO `table` SET `column1`='value1', ....
You can do something like this instead of passing json object
"INSERT INTO projects (uid, title, created) VALUES (1, 'Michelle', 'Blue Village 1')";
e.g in your case you can use string interpolation:
`INSERT INTO projects (uid, title, created) VALUES ('${request.session.uid}', '${request.body.title}', '${request.body.createdAt}')`;

Why do I get syntax error for "INSERT INTO"?

String pName = getStrFromUser("Product name: ");
int price = getIntFromUser("Price: ", false);
String category = getStrFromUser("Category: ");
String description = getStrFromUser("Description: ");
PreparedStatement statement = connection.prepareStatement("INSERT INTO ws.products (name, price, cid, description) VALUES (?, ?, (SELECT ws.categories.cid FROM ws.categories WHERE ws.categories.name LIKE ?), ?)");
statement.setString(1, pName);
statement.setInt(2, price);
statement.setString(3, category);
statement.setString(4, description);
statement.executeUpdate();
I get:
Error encountered: ERROR: syntax error at or near "INSERT INTO ws
What might be the problem?
The subquery inside the VALUES clause looks suspicious. Try rephrasing as an INSERT INTO ... SELECT:
String sql = "INSERT INTO ws.products (name, price, cid, description) ";
sql += "SELECT ?, ?, cid, ? FROM ws.categories WHERE name LIKE ?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1, pName);
statement.setInt(2, price);
statement.setString(3, description);
statement.setString(4, category);
statement.executeUpdate();
I would recommend insert . . . select:
INSERT INTO ws.products (name, price, cid, description)
SELECT ?, ?, ws.categories.cid, ?
FROM ws.categories
WHERE ws.categories.name LIKE ?;
This will not fix the problem with INSERT, but it will prevent the next problem of a subquery returning more than one row.
My best guess for that problem is that the library you are using only supports SELECT statements. That would be atypical; INSERT is usually allowed.

MockMVC not mapping query parameters

When I test REST API endpoints using POSTMAN, It works fine. This is the console message.
path : -1
****** doFilter!!!!!!!!!!!!!!!!!!!
2018-11-15 10:52:56,949 DEBUG [com.isu.ifm.wrapper.RequestWrapper::<init>:85] URI : /ifm/Board.do
2018-11-15 10:52:56,949 DEBUG [com.isu.ifm.wrapper.RequestWrapper::<init>:86] METHOD : GET
2018-11-15 10:52:56,950 DEBUG [com.isu.ifm.wrapper.RequestWrapper::<init>:126] PARAM : comments : [a]
enterCd : [ISU_PS]
bbsCd : [10003]
bbsSeq : [1]
cmd : [saveCmt]
sabun : [91006]
preHandler ::::::::::::::http://localhost:8083/ifm/Board.do
ISU_PS
91006
preHandler ::::::::::::::/ifm/Board.do
[2018-11-15 10:52:56] [DEBUG]『org.apache.commons.dbcp.PoolingDataSource:getConnection(106)』 Elapsed Time [0:00:00.007]
select
1
from
dual
[2018-11-15 10:52:56] [DEBUG]『org.springframework.jdbc.core.JdbcTemplate:execute(644)』 Elapsed Time [0:00:00.023]
INSERT INTO TSYS730(ENTER_CD, SABUN, BBS_SEQ, COMMENTS_SEQ, COMMENTS, CHKDATE, CHKID, BBS_CD)
VALUES( 'ISU_PS' /**PARAM*/, '91006' /**PARAM*/, '1' /**PARAM*/, BOARD_COMTSEQ.nextval, 'a' /**PARAM*/, sysdate, '91006' /**PARAM*/, '10003' /**PARAM*/)
Query Result[INSERT]: 1row
2018-11-15 10:52:56,984 DEBUG [com.isu.ifm.wrapper.ResponseWrapper::toByteArray:95] RETURN VALUE : {"message":"","map":{"comments":"a","enterCd":"ISU_PS","bbsCd":"10003","bbsSeq":"1","cmd":"saveCmt","sabun":"91006","ssnEnterCd":"ISU_PS","ssnSabun":"91006"},"code":1}
But when I try to test same endpoints using jUnit with MockMVC, it gets parameters but cannot map in query.
ERROR [org.anyframe.query.QueryService::processException:1889] Query Service : Fail to [update [query id = 'saveCmt'],
INSERT INTO TSYS730(ENTER_CD, SABUN, BBS_SEQ, COMMENTS_SEQ, COMMENTS, CHKDATE, CHKID, BBS_CD)
VALUES(:ssnEnterCd, :ssnSabun, :bbsSeq, BOARD_COMTSEQ.nextval, :comments, sysdate, :ssnSabun, :bbsCd)
, PreparedStatementCallback; SQL [
INSERT INTO TSYS730(ENTER_CD, SABUN, BBS_SEQ, COMMENTS_SEQ, COMMENTS, CHKDATE, CHKID, BBS_CD)
VALUES(?, ?, ?, BOARD_COMTSEQ.nextval, ?, sysdate, ?, ?)
]; ORA-01400: cannot insert NULL in ("EHR_ISU4"."TSYS730"."ENTER_CD")
; nested exception is java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL in ("EHR_ISU4"."TSYS730"."ENTER_CD")
].
Query = [org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [
INSERT INTO TSYS730(ENTER_CD, SABUN, BBS_SEQ, COMMENTS_SEQ, COMMENTS, CHKDATE, CHKID, BBS_CD)
VALUES(?, ?, ?, BOARD_COMTSEQ.nextval, ?, sysdate, ?, ?)
]; ORA-01400: cannot insert NULL in ("EHR_ISU4"."TSYS730"."ENTER_CD")
; nested exception is java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL in ("EHR_ISU4"."TSYS730"."ENTER_CD")
]
Reason = [{}].
MockHttpServletRequest:
HTTP Method = GET
Request URI = /Board.do
Parameters = {cmd=[saveCmt], enterCd=[ISU_PS], sabun=[91006], bbsCd=[10001], bbsSeq=[1], comments=[a]}
Headers = {}
Handler:
Type = com.isu.ifm.controller.BoardController
Method = public org.springframework.web.servlet.ModelAndView com.isu.ifm.controller.BoardController.saveCmt(javax.servlet.http.HttpSession,javax.servlet.http.HttpServletRequest,java.util.Map<java.lang.String, java.lang.Object>) throws java.lang.Exception
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = jsonView
View = null
Attribute = code
value = -1
Attribute = message
value = fail
Attribute = map
value = {cmd=saveCmt, enterCd=ISU_PS, sabun=91006, bbsCd=10001, bbsSeq=1, comments=a, ssnEnterCd=null, ssnSabun=null}
FlashMap:
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = jsonView
Redirected URL = null
Cookies = []
2018-11-15 10:49:19,788 INFO [org.springframework.context.support.GenericApplicationContext::doClose:862] Closing org.springframework.context.support.GenericApplicationContext#9660f4e: startup date [Thu Nov 15 10:49:14 KST 2018]; root of context hierarchy
Here is my testcase code.
public MockMvc mockMvc;
public void testGetMethod_param(String url, UnaryOperator<MockHttpServletRequestBuilder> operator)
throws Exception {
mockMvc.perform(operator.apply(get(url))).andDo(print()).andExpect(status().isOk());
}
#ContextConfiguration("file:src/main/webapp/WEB-INF/**/*.xml")
public class BoardControllerTest extends HttpRequestTestMethod implements CoreParameters, AdapterServiceParameters{
#Autowired
public BoardController boardController;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(boardController).build();
}
#Test
public void savecmt() throws Exception {
testGetMethod_param("/Board.do", builder->builder
.param("cmd", "saveCmt")
.param("enterCd", enterCd)
.param("sabun", sabun)
.param("bbsCd", bbsCd)
.param("bbsSeq", bbsSeq)
.param("comments", comments));
}
If I deliberately set enterCd to NULL, I get this following message.
[2018-11-15 11:06:02] [ERROR]『oracle.jdbc.driver.T4CTTIoer:processError(439)』
java.sql.SQLIntegrityConstraintViolationException
INSERT INTO TSYS730(ENTER_CD, SABUN, BBS_SEQ, COMMENTS_SEQ, COMMENTS, CHKDATE, CHKID, BBS_CD)
VALUES( null /**PARAM*/, '91006' /**PARAM*/, '1' /**PARAM*/, BOARD_COMTSEQ.nextval, 'a' /**PARAM*/, sysdate, '91006' /**PARAM*/, '10003' /**PARAM*/)
ORA-01400: cannot insert Null in ("EHR_ISU4"."TSYS730"."ENTER_CD")
But as attached above, when using jUnit, all parameters are set to "?"
Query = [org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [
INSERT INTO TSYS730(ENTER_CD, SABUN, BBS_SEQ, COMMENTS_SEQ, COMMENTS, CHKDATE, CHKID, BBS_CD)
VALUES(?, ?, ?, BOARD_COMTSEQ.nextval, ?, sysdate, ?, ?)
]; ORA-01400: cannot insert Null in ("EHR_ISU4"."TSYS730"."ENTER_CD")
; nested exception is java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert Null in ("EHR_ISU4"."TSYS730"."ENTER_CD")
]
Can anyone tell me what is wrong here?
Any help would be greatly appreciated.

SQL Hibernate error

I'm using a helper class and trying to insert some data into a db table. I have one self join class with a onetomany relationship.
The main class for the insert transaction
public static void main(String[] args) {
// TODO Auto-generated method stub
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
// Create new checker
Staff checker = new Staff();
checker.setStaffId("d8");
checker.setEmail("do#msn.com");
checker.setName("Doris");
checker.setPassword("890");
checker.setUsername("dr89");
// Create new staff
Staff staff1 = new Staff();
staff1.setStaffId("m6");
staff1.setEmail("mr#msn.com");
staff1.setName("Marius");
staff1.setPassword("234");
staff1.setUsername("mr23");
Staff staff2 = new Staff();
staff2.setStaffId("b8");
staff2.setEmail("be#msn.com");
staff2.setName("Betty");
staff1.setPassword("567");
staff1.setUsername("be56");
Set<Staff> staff = new HashSet<Staff>();
staff.add(staff1);
staff.add(staff2);
staff1.setChecker(checker);
staff2.setChecker(checker);
checker.setSetters(staff);
session.save(checker);
session.save(staff1);
session.save(staff2);
transaction.commit();
}catch (HibernateException e) {
transaction.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
the DB
CREATE TABLE `staff` (
`staff_id` VARCHAR(255) NOT NULL,
`name` VARCHAR(255) NULL DEFAULT NULL,
`username` VARCHAR(255) NOT NULL,
`password` VARCHAR(255) NOT NULL,
`email` VARCHAR(255) NULL DEFAULT NULL,
`checker_id` VARCHAR(255) NULL DEFAULT NULL,
PRIMARY KEY (`staff_id`),
FOREIGN KEY (`checker_id`) REFERENCES `staff` (`staff_id`));
After I ran the code, it came up with a constraint violation at the column password.
The query and warning in the stacktrace:
Hibernate: select staff_.staff_id, staff_.checker_id as checker6_2_, staff_.email
as email2_, staff_.name as name2_, staff_.password as password2_, staff_.username as
username2_ from staff staff_ where staff_.staff_id=?
Hibernate: select staff_.staff_id, staff_.checker_id as checker6_2_, staff_.email as
email2_, staff_.name as name2_, staff_.password as password2_, staff_.username as
username2_ from staff staff_ where staff_.staff_id=?
Hibernate: insert into staff (checker_id, email, name, password, username, staff_id)
values (?, ?, ?, ?, ?, ?)
Hibernate: insert into staff (checker_id, email, name, password, username, staff_id)
values (?, ?, ?, ?, ?, ?)
Hibernate: insert into staff (checker_id, email, name, password, username,
staff_id)
values (?, ?, ?, ?, ?, ?)
WARN : org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Error: 1048, SQLState:
23000
ERROR: org.hibernate.engine.jdbc.spi.SqlExceptionHelper - Column 'password' cannot
be null
The full error
org.hibernate.exception.ConstraintViolationException: Column 'password' cannot be null
at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:74)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110)
at org.hibernate.engine.jdbc.internal.proxy.AbstractStatementProxyHandler.continueInvocation(AbstractStatementProxyHandler.java:129)
at org.hibernate.engine.jdbc.internal.proxy.AbstractProxyHandler.invoke(AbstractProxyHandler.java:81)
at $Proxy16.executeUpdate(Unknown Source)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:56)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3028)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3469)
at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:88)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:362)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:354)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:275)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:326)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:52)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1213)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:402)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
at com.project.professional.StaffRelationshipTest.main(StaffRelationshipTest.java:75)
Yes, I set the password in the table to not null, and I have added some data to put it in the main class, so it should not be null. So I'm not sure why the error.
Typo;
Staff staff2 = new Staff();
staff2.setStaffId("b8");
staff2.setEmail("be#msn.com");
staff2.setName("Betty");
staff1.setPassword("567"); << should be staff2
staff1.setUsername("be56"); << should be staff2
Since the password is never set on staff2, the insert (rightly so) fails.