I wanted to find out if there is a way to know the query Spring Boot creates to extract data from a db. In detail: given Spring Boot needs to map a POJO to several tables in a db, how does it create the joints to provide the resulting JSON? Is there a way to see the query it generates in a log?
You can add below configuration in application.properties to see the query generated by Spring Boot / Hibernate
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.show_sql=true
hibernate.show_sql=true
hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.*=true
You can see the query in console. Here I am sharing a sample of it.
2019-07-10 11:46:38:823 [http-nio-8080-exec-7] DEBUG org.hibernate.SQL -
select
*
from
user
where
statusenable=true
Hibernate:
select
*
from
user
where
statusenable=true
If you have passed parameters to query you can also see it (last line)
2019-07-10 11:49:30:073 [http-nio-8080-exec-3] DEBUG org.hibernate.SQL -
select
*
from
user
where
id=?
and statusenable=true
Hibernate:
select
*
from
user
where
id=?
and statusenable=true
2019-07-10 11:49:30:089 [http-nio-8080-exec-3] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [NUMERIC] - [2]
Related
I have a common setup with a rest controller calling method from a service which itself calls a repository(extends JpaRepository) method(simple select through #Query annotation).
As I do a select it's beeing recommended to annotate method in the service with #Transactional(readOnly = true).
After enabling two different logs I can see the following:
logging.level.org.springframework.transaction.interceptor=TRACE
o.s.t.i.TransactionInterceptor : Getting transaction
for [mypackage.MyService.MyMethod]
o.s.t.i.TransactionInterceptor : No need to create transaction for
[org.springframework.data.jpa.repository.support.SimpleJpaRepository.MyMethod]:
This method is not transactional. Hibernate: select {here goes my
select statement} o.s.t.i.TransactionInterceptor :
Completing transaction for [mypackage.MyService.MyMethod]
logging.level.org.springframework.orm=DEBUG
o.s.orm.jpa.JpaTransactionManager: Creating new transaction with name
[mypackage.MyService.MyMethod]:
PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
o.s.orm.jpa.JpaTransactionManager: Opened new EntityManager
[SessionImpl(1445729846)] for JPA transaction
o.s.orm.jpa.JpaTransactionManager: Exposing JPA transaction as JDBC
[o.s.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle#ea0fe1c]
Hibernate: select {here goes my select statement}
o.s.orm.jpa.JpaTransactionManager: Initiating transaction commit
o.s.orm.jpa.JpaTransactionManager : Committing JPA
transaction on EntityManager [SessionImpl(1445729846)]
o.s.orm.jpa.JpaTransactionManager : Closing JPA
EntityManager [SessionImpl(1445729846)] after transaction
and if change my service method annotaion to #Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
In log I can see only one line:
o.s.orm.jpa.EntityManagerFactoryUtils: Opening JPA EntityManager
So quite misleading logs for the same method. Is there actually a transaction created?
It seems that we do not need one for such cases(simple selects) to not create a persistence context and save some dirty checking costs.
But when what be the isolation rules in such cases which data could be read from database?
I have a Spring Boot project with different databases for dev (MySQL) and test (H2 in memory). I also have two data.sql files configured for dev and test profiles.
In test when Hibernate creates database tables, it uses lower-case table names. After database creation, app tries to fill database with insert queries inside data-test.sql file, it looks for tables in UPPER_CASE and failes:
Hibernate create table sql:
create table "activity_domain" (...)
data-test.sql:
INSERT INTO activity_domain (title) VALUES ('Activity Domain One');
Error message:
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "ACTIVITY_DOMAIN" not found (candidates are: "activity_domain"); SQL statement:
INSERT INTO activity_domain (title) VALUES ('Activity Domain One') [42103-212]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:502) DbException.java:502
at org.h2.message.DbException.getJdbcSQLException(DbException.java:477) DbException.java:477
at org.h2.message.DbException.get(DbException.java:223) DbException.java:223
at org.h2.command.Parser.getTableOrViewNotFoundDbException(Parser.java:8379) Parser.java:8379
at org.h2.command.Parser.getTableOrViewNotFoundDbException(Parser.java:8347) Parser.java:8347
at org.h2.command.Parser.readTableOrView(Parser.java:8336) Parser.java:8336
at org.h2.command.Parser.readTableOrView(Parser.java:8306) Parser.java:8306
at org.h2.command.Parser.parseInsert(Parser.java:1641) Parser.java:1641
at org.h2.command.Parser.parsePrepared(Parser.java:814) Parser.java:814
at org.h2.command.Parser.parse(Parser.java:691) Parser.java:691
at org.h2.command.Parser.parse(Parser.java:661) Parser.java:661
at org.h2.command.Parser.prepareCommand(Parser.java:568) Parser.java:568
at org.h2.engine.SessionLocal.prepareLocal(SessionLocal.java:631) SessionLocal.java:631
at org.h2.engine.SessionLocal.prepareCommand(SessionLocal.java:554) SessionLocal.java:554
at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1116) JdbcConnection.java:1116
at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:237) JdbcStatement.java:237
at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:223) JdbcStatement.java:223
at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261)
More info:
application.properties for test profile:
spring.datasource.url=jdbc:h2:mem:my-test-db;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.defer-datasource-initialization=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
spring.sql.init.data-locations=classpath:data-test.sql
DATABASE_TO_UPPER=FALSE is an option suitable only for very old versions of H2 Database. It works in different way with modern versions and it effectively makes all identifiers case sensitive. It doesn't enable case insensitive identifiers any more, they are controlled by another option.
With modern versions if you need to have some compatibility with MySQL you need to add ;MODE=MySQL;DATABASE_TO_LOWER=TRUE instead. You may also need ;CASE_INSENSITIVE_IDENTIFIERS=TRUE, see documentation of compatibility modes for mode details:
https://h2database.com/html/features.html#compatibility
In my application I am using the following #JoinFormula
#JoinFormula(value="(select s.\"Id\" from \"Student\" s where ash.\"Class_Id\" = id)" )
However, at runtime the generated query is:
(
select
s.cla0_."Id"
from
cla0_."Student" s
where
s.cla0_."Class_Id" = cla0_.id) as formula4_0_,
I need to prevent adding cla0_ table alias to the formula query. How could it be done in hibernate?
I am using it in spring boot, and postgres as database.
I have a spring boot 2 application that works great with entities that are mapped to a table that is in a MariaDB. I now do a view call and I have mapped that entity to the view using the #Table(name="ViewExpiredAccounts") annotation and now when the method is called the table name is ignored. I have a JpaRepository with this method:
#Query(value = "SELECT v FROM ViewExpiredAccounts v")
List<ViewExpiredAccount> expiredAccounts();
When I call this method I get an error:
Table 'view_expired_accounts' doesn't exist
The query SHOULD! translate the table name so that the eventual SQL query sent to MariaDB is: SELECT * from ViewExpiredAccount however it doesnt do that. Is this a bug in Spring??
did you reset the hibernate naming strategy?
if yes, the following property might help
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
I have a spring boot application where I use Spring Data JPA with PostgreSQL as the database. I'm letting hibernate create the schema and that is working perfectly, but when I'm trying to populate the database with the import.sql file, an error shows up.
This is the error:
CommandAcceptanceException: Error executing DDL "insert into users(user_id, username, email, password, created_at)" via JDBC Statement
Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at end of input.
This is my import.sql file:
insert into users(user_id, username, email, password, created_at)
values(1, 'alex', 'alex#email.com', 'pword', current_timestamp);
This is my application.properties file:
spring.datasource.url=jdbc:postgresql://localhost:5432/website
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQL95Dialect
spring.datasource.username=alexander
spring.datasource.password=
spring.jpa.show-sql=true
# drop n create table again, good for testing, comment this in production
spring.jpa.hibernate.ddl-auto=create-drop
# spring.sql.init.data-locations= classpath:/data.sql
# spring.datasource.initialization-mode=always
# spring.sql.init.mode=always
# spring.jpa.defer-datasource-initialization=true
The commented out part in application.properties are things that I've found in other threads that I've tried but with no change in result.
Every table gets created perfectly, and if I copy the contents of import.sql and inserts it into PostgreSQL via the terminal or pgadmin, then it works perfectly and the user gets inserted with 0 problems. So I really don't get what this "syntax error at end of input." could be.
PS. It's not an issue with the word current_timestamp either, if I swap it with for example ('2020-05-06 08:30') I still get the exact same error.
You have better solution for this problem than moving SQL insert into one line. Just add jpa property in application.properties which allows you to put SQL statements in multiple lines:
jpa:
properties:
hibernate.hbm2ddl.import_files_sql_extractor: org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor
Having SQL insert statement in two (or multiple) rows can improve readability