How to modify DB schema after generation but before test execution? - java

Long story short
I want to generate DB schema from Hibernate mappings and then replace a specific table with a view of the same name before the application starts.
How can I do this using Spring / Hibernate / DbUnit / JDBC or something else?
My problem in details
I have a few integration tests that are executed against an in-memory database.
There's an AView view in a real database and it's mapped in Java code as
#Entity #Table #Immutable
public class AView {}
I'm generating H2 DB schema from Hibernate mappings for integration tests. And during test application context initialization this view is created as a table. From logs:
Hibernate: drop table AView if exists
Hibernate: create table AView (...)
Some tests fail because of this.
The idea
In order to fix this, I want to make H2 DB schema as similar as possible to the real DB schema. First, I want to generate DB schema from Hibernate mappings, and then replace AView table with AView view.
What I have tried
I have found a similar question: How to execute sql script after db schema generation but before application startup
I created a file schema.sql with DROP TABLE / CREATE VIEW statements. I tried to put the file in src/test/resources/schema.sql but it's not picked up automatically by Spring. I tried to specify this file explicitly in #Sql annotation, but it still doesn't have a visible effect.
I execute the tests via IntelliJ IDEA (if this is important).
My Code
Test and Test application context:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { TestH2Config.class })
#TransactionConfiguration(defaultRollback = true)
#Transactional
public class AViewServiceIT {}
#Configuration
#PropertySource({"classpath:datasource-h2.properties"})
#EnableTransactionManagement
//#Sql({"/schema.sql"})
public class TestH2Config {}
datasource-h2.properties
datasource.driverClassName=org.h2.Driver
datasource.url=jdbc:h2:mem:itest;MODE=MSSQLServer;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS itest\\;SET SCHEMA itest
datasource.username=sa
datasource.password=
hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.show_sql=true
hibernate.hbm2ddl.auto=create-drop
Spring framework version is 4.1.9.RELEASE.

You should be able to use the import.sql file in the root of your classpath as a means to have Hibernate execute a set of SQL commands after the schema has been built. Given that you wish to do this specifically for tests only, placing it in the test root classpath should be sufficient.

Related

How to create a view or table from an Entity?

I have a question regarding views in the context of jpa entities in SpringBoot. Up to now I am using the auto create feature that automatically creates the tables by the definitions of the entities in Java. Now my application has grown so far that I need to use views. I do not want to write and maintain the sql create statements for all tables/entities otherwise I could simple add the create view statement to the schema.sql file, which I do not want to use. Instead I have a commandLineRunner that creates the views after startup but when testing the app it fails because the entities reference the views before the idividual views are created.
So is there a way to write an sql create statement in the entity maybe with an annotation to create a view during entity instantiation?
On startup you can initialize a data bootstrap. I got the code from here, which basically is a void method with your repository Autowired where you are able to create and load your data on startup of Spring.
I have implemented this dataloader in my own code and you can find this on my public GitHub.
By using an abstract class you can extend the methods (to seperate dev from prod class loaders) and with IoC you set the repository and load the data. I use CRUD with Redis, but it's pretty universal.
private final PriceRepository priceRepository;
#Autowired
public ProductionDataLoader(PriceRepository priceRepository, KeywordRepository keywordRepository, AccountRepository accountRepository) {
this.priceRepository = priceRepository;
}
#Override
public void loadEnvironmentSpecificData() {
doSomethingWithData();
}
By using #Profile annotations you can seperate dev from prod for example.
This is a hobby project of mine, I'm in no way a (certified) developer...
I found a very simple way, how to create a view without having to create all tables that have been managed by JPA with the entity instantiation automatically.
Basically I let spring boot start up and create all tables. This includes creating a table with the same name as the desired view by the JPA entity.
After startup I simply drop the table and then create my own view in an sql script.
It works very well and the entity keeps using the view after the table is dropped because they both have the same name.
Here is some code:
public class StartUpRunner implements CommandLineRunner {
public static final String VIEW_INIT_FILE = "after_hibernate_init.sql";
#Autowired
private DataSource dataSource;
#Override
public void run(String... arg) throws Exception {
createSQLViews();
}
private void createSQLViews(){
boolean IGNORE_FAILED_DROPS = true;
ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator(false, IGNORE_FAILED_DROPS , "UTF-8", new ClassPathResource(VIEW_INIT_FILE));
resourceDatabasePopulator.execute(dataSource);
}
}
And in the sql file there should be something like:
DROP TABLE IF exists YOUR_VIEW_NAME;
CREATE OR REPLACE View YOUR_VIEW_NAME
//Your view creation statement here....
It is very important to set the flag "ignore failed drops" to true because after the first startup the view will already exist and the sql script fails on the drop tables statement which would shut down the application. This way SpringBoot ignores the failed statement and starts up normally.
A downside to this approch is that you cannot test the view with #DataJpaTest anymore since the StartUpRunner needs to create the view. At least if you are like me and use the embedded H2 database from SpringBoot, which needs to be initialized before every test class.
My test annotation for testing the views looks like this:
#ActiveProfiles("sqltest")
#ExtendWith(SpringExtension.class)
#SpringBootTest
#TestMethodOrder(MethodOrderer.OrderAnnotation.class)
I am using the testMethodOrder because the sql inserted data is not cleared after each test anymore and I only insert the data before the first test and use it in all tests.
The activeProfiles annotation should be relatively self-explanatory. There I specify the test H2 database and other app specific settings.
Feel free to ask more about this approach on how to squash views into the auto-create feature of JPA.

Spring boot SQL script manually

I got a task, to create a basic app with some database handling.
I'm really new at this, the whole spring boot and stuff.
I did all the GET, PUT, POST, DELETE stuff without writing any line of SQL, starts with table creation.
But they asked me to provide the SQL script, how I managed to create the database structure, connections and stuff.
How can I solve this problem?
Add following to spring.jpa.properties
spring.jpa.properties.javax.persistence.schema-generation.scripts.action=create
spring.jpa.properties.javax.persistence.schema-generation.scripts.create-target=create.sql
spring.jpa.properties.javax.persistence.schema-generation.scripts.create-source=metadata
Please refer this article https://www.baeldung.com/spring-data-jpa-generate-db-schema by #baeldung for more detail.
The most simple way to do this is to create in the resources folder a file named schema.sql with all your queries concerning the DDL (tables creation...) and a data.sql if needed where you can add INSERT queries to populate your database.
In the application.properties then you have to add spring.jpa.hibernate.ddl-auto=none to disable the schema creation of JPA and let Spring Boot executes those scripts at startup automatically.
If you want something more powerful, you should try Liquibase or Flyway.
You can used Jpa Like JpaRepository interface. You don't need write any SQL query we use it just used config your data base into Application.Property like database type
Your user name and password
public interface UserRepository extends CrudRepository<User, Integer> {}
when execute this code you can used create user , get find all user , find user by id , delete user and update user
and used #Entity annotaion into Entity class
the Entity class Represent Table into your database
#Entity
#Table(name="user")
public class User{
#Colume(name="id")
private Long id;
#Colume(name="name")
private String name;
//getter and setter here
}
when run this class your data base contract User table with id and
name
this link create spring boot application with database
https://spring.io/guides/gs/accessing-data-mysql/

HSQLDB: case sensitive table names

I have the following jpa/hibernate/hsqldb configuration:
JPA ddl-auto: create-drop
Hibernate entities have no #Table annotation and created with SpringPhysicalNamingStrategy. So, PersonalData entity table name is personal_data. Hibernate creates them due to running the application
hsql DB URL is jdbc:hsqldb:mem:testdb;sql.syntax_pgs=true
My problem is when I try to select due spring repositories with the hsql there is the error about non-existing PERSONAL_DATA table.
I found that this is SQL notation to use CASE_SENSETIVE tables and hqsl follows that. To resolve that developers offer quote table names in sql.
So, I have 2 unlikely ideas
Add #Table annotation to entities.
Override SpringPhysicalNamingStrategy
Is there a way to use a simple property?

Using #Entity Class When Table Doesn't Exist

I have several database tables that my Spring MVC/JPA application refers to using the #Entity and #Table Annotations. I've run into the issue where if my application switches between database connections, some tables that exist on database 1 may not exist in database 2 (as we are following the SDLC cycle and promoting table additions/changes after they get the "OK"), thus resulting in an SQL Exception when the application server starts.
Does spring offer a way to mark specific #Entity Classes as "Optional" or "Transactional" so there are no database Exceptions returned because of nonexistant tables?
In my opinion, there is no option to do that.
You can add automatic update of schema in Hibernate, but you mentioned that you are doing this manually.
Hibernate is validating the schema, when he establishes connection. You use #Entity, so he looks for that table and throws an error if there is no with the name specified.

QueryDsl Set Schema

Using a Spring DataSource and a QueryDsl SQLQueryImpl, how do you specify the schema or set the schema?
Test case is:
Use querydsl-maven-plugin (2.9.0) to connect to DB2 database to generate Q-Classes specifying <schemaPattern>
Spring creates a DataSource bean
DataSource bean is populated by scripts (CREATE SCHEMA FOO, CREATE TABLE FOO.BAR etc)
JUnit test imports DataSource
Query using new SQLQueryImpl(dataSource.getConnection(), Configuration.DEFAULT)
Error:
org.h2.jdbc.JdbcSQLException: Table "BAR" not found; SQL Statement: ...
Note that this test implementation is an H2 database, but the production database is DB2. So far I've tried specifying the schema in the JDBC connection strings to no avail.
com.mysema.query.sql.SQLTemplates has a builder method called printSchema(). Initialise SQLTemplates using the builder() method and call printSchema() before build(). Example to follow:
SQLTemplates templates = OracleTemplates.builder().printSchema().build();
The Schema is encoded in the Q-types. It is accessed in the SQL serialization via the RelationalPath interface http://www.querydsl.com/static/querydsl/2.9.0/apidocs/com/mysema/query/sql/RelationalPath.html
I was able to extend SQLTemplates with my own implementation that called the protected method setPrintSchema(true);.

Categories

Resources