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.
Related
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/
I have a requirement to capture changes to certain data. I am looking to capture the following details:
Table Name the change occurred in
Column Changed
Previous Value
Updated Value
I suggested to our Technical lead that this can be accomplished easily with DB Triggers. I was told we do not control the DB and do not have a way of adding Triggers.
I am currently using Spring AspectJ with a custom annotation and wrapping my services to try and capture the generated SQL (I figure parsing SQL is much easier that trying to capture with Objects) that's executed after the 'save' method is called, however I have not found a way to trap the generated SQL.
I tried p6Spy and was able to view the SQL and print it to the console, but was told we cannot wrap our db drivers in our PROD environment.
Is there a Spring class I am missing to make this easier?
EDIT : We're using Spring Repositories to save the data.
EDIT 2: I'm looking into EventListeners, however I cannot seem to get them to listen to my events.
#Component
public class EventListner implements PreInserEventListener, PreUpdateEventListener {
#Override
#EventListener
public boolean onPreUpdate(PreUpdateEvent event){
// do something
return false;
}
#Override
#EventListener
public boolean onPreInsert(PreUpdateEvent event){
// do something
}
return false
}
I have break points around my Listener, however they're never reached.
This question looks like it might address my issue
If you are absolutely sure that every insert/update/delete is done through JPA without any bulk SQL then you should take a look at envers
With that you can use #Audited on the entities(all column) OR columns of entities you wish to keep history.
This will create 1 revision table with timestamp change (and other data you want like uid of the user that made the change), and 1 table per entity with the old value of the modified data.
For every data you can then retrieve the history and previous value.
one other way is to add instrument on your DB like a Change Data Capture (CDC) tools and push those event to another data repository.
On the + side everything will be detected (even native SQL run directly on the DB). Drawback, your DB need to support this kind of tools correctly. For instance tools like kafka-connect can work like you want, but some implementations are too simple (like with SAP hana for instance where the process is to do a select * from xxx).
I finally settled on using Hibernate interceptors to do the trick. Works perfectly!
public class TableInterceptor extends EmptyInterceptor {
#Override
public boolean onFlushDirty(Object entity, Serializable id,
Object[] currentState, Object[] previousState,
String[] propertyNames, Type[] types){
// do comparison logic on fields and save to DB
}
}
I bootstrapped the interceptor injecting it into my LocalContainerEntityManagerFactoryBean
I have the following question. From what I understand the #Transactional annotation is supposed to keep the session alive, thus enabling to lazy fetch child entities without the need to performe a specific joining query.
I have the following scenario where I do not understand why I'm still getting a LazyInitializationException.
My app runs a resolver in order to provide the various controller services with a resolved object so that it can be used directly.
Said resolver intercepts a header from the request and using it's value attempts to query the db in order to fetch the object. Now the object in question is quite simple is it's doings albeit it has a list of two sub-entities.
In order to perform the resolving action I'm using an extra service where I basically wrap some JpaRepository methods. The complete is below:
#Service
public class AppClientServiceImpl implements AppClientService {
private static final Logger LOGGER = LoggerFactory.getLogger(AppClientServiceImpl.class.getCanonicalName());
private final AppClientRepository repository;
#Autowired
public AppClientServiceImpl(AppClientRepository repository) {
this.repository = repository;
}
#Override
#Transactional(readOnly = true)
public AppClient getByAppClientId(final String appClientId) {
LOGGER.debug("Attempting to retrieve appClient with id:: {}", appClientId);
return repository.findByAppClientId(appClientId);
}
#Override
#Transactional
public void saveAndFlush(final AppClient appClient) {
LOGGER.debug("Attempting to save/update appClient:: {}", appClient);
repository.saveAndFlush(appClient);
}
}
As you can see both methods are annotated as #Transactional meaning that the should keep the session alive in the context of that said method.
Now, my main questions are the following:
1) Using the debugger I'm seeing even on that level getByAppClientId the list containing on the sub-entities which is lazy loaded has been resolved just fine.
2) On the resolver itself, where the object has been received from the delegating method, the list fails to be evaluated due to a LazyInitializationException.
3) Finally on the final controller service method which is also marked as #Transactional, the same as above occurs meaning that this eventually fails to it's job (since it's performing a get of the list that has failed to initialize.
Based on all the above, I would like to know what is the best approach in handling this. For once I do not want to use an Eager fetching type and I would also like to avoid using fetch queries. Also marking my resolver as #Transactional thus keeping the session open there as well is also out of the question.
I though that since the #Transactional would keep the session open, thus enabling the final service method to obtain the list of sub-entities. This seems not to be the case.
Based on all the above it seems that I need a way for the final service method that gets call (which needs the list on hand) to fetch it somehow.
What would the best approach to handle this? I've read quite a few posts here, but I cannot make out which is the most accepted methods as of Spring boot 2.0 and hibernate 5.
Update:
Seems that annotating the sub-entitie with the following:
#Fetch(FetchMode.SELECT)
#LazyCollection(LazyCollectionOption.TRUE)
Resolves the problem but I still don't know whether this is the best approach.
You initialize the collection by debugging. The debugger usually represents collections in a special way by using the collection methods which trigger the initialization, so that might be the reason why it seems to work fine during debugging. I suppose the resolver runs outside of the scope of the getByAppClientId? At that point the session is closed which is why you see the exception.
I created Blaze-Persistence Entity Views for exactly that use case. You essentially define DTOs for JPA entities as interfaces and apply them on a query. It supports mapping nested DTOs, collection etc., essentially everything you'd expect and on top of that, it will improve your query performance as it will generate queries fetching just the data that you actually require for the DTOs.
The entity views for your example could look like this
#EntityView(AppClient.class)
interface AppClientDto {
String getName();
}
Querying could look like this
List<AppClientDto> dtos = entityViewManager.applySetting(
EntityViewSetting.create(AppClientDto.class),
criteriaBuilderFactory.create(em, AppClient.class)
).getResultList();
I have a static method in the entity
#Transactional
public static void updateState() {
entityManager().createNativeQuery("UPDATE TABLEA SET hide = 1 WHERE id= 1").executeUpdate();
}
But when I call the method, I catch a exception say the update statement need a transaction.
Am I using the #Transactional in the wrong way?
It seems like you are trying to make your Entity a fat domain model (as opposed to thin models that are most common in the Java EE world) following the Active Record pattern.
What you are trying to do will not work as is in Spring.
If you refactor your method to not be static (first problem) then one way to get #Transactional working on a JPA entity is to use the #Configurable annotation from Spring (making it managed by Spring - therefore fixing the second problem), along with load time weaving and a Java agent. See this and this for more details.
Maybe you should try with the annotation:
#Transactional(readOnly=false)
I'm implementing several DAO classes for a web project and for some reasons I have to use JDBC.
Now I'd like to return an entity like this:
public class Customer{
// instead of int userId
private User user;
// instead of int activityId
private Activity act;
// ...
}
Using JPA user and activity would be loaded easily (and automatically specifying relations between entities).
But how, using JDBC? Is there a common way to achieve this? Should I load everiting in my CustomerDAO? IS it possible to implement lazy initialization for referenced entities?
My first idea was to implement in my UserDAO:
public void initUser(Customer customer);
and in my ActivityDAO:
public void initActivity(Customer customer);
to initialize variables in customer.
Active Record route
You could do this with AspectJ ITDs and essentially make your entities into Active Record like objects.
Basically you make an Aspect that advises class that implement an interface called "HasUser" and "HasActivity". Your interfaces HasUser and HasActivity will just define getters.
You will then make Aspects that will weave in the actual implementation of getUser() and getActivity().
Your aspects will do the actual JDBC work. Although the learning curve on AspectJ is initially steep it will make your code far more elegant.
You can take a look at one of my answers on AspectJ ITD stackoverflow post.
You should also check out springs #Configurable which will autowire in your dependencies (such as your datasource or jdbc template) into non managed spring bean.
Of course the best example of to see this in action is Spring Roo. Just look at the AspectJ files it generates to get an idea (granted that roo uses JPA) of how you would use #Configurable (make sure to use the activerecord annotation).
DAO Route
If you really want to go the DAO route than you need to this:
public class Customer{
// instead of int userId
private Integer userId;
// instead of int activityId
private Integer activityId;
}
Because in the DAO pattern your entity objects are not supposed to have behavior. Your Services and/or DAO's will have to make transfer objects or which you could attach the lazy loading.
I'm not sure if there is any automated approach about this. Without ORM I usually define getters as singletons where my reference types are initialized to null by default, i.e. my fetching function would load primitives + Strings and will leave them as null. Once I need getUser(), my getter would see if this is null and if so, it would issue another select statement based on the ID of the customer.