Spring batch - hibernate integration - spring

i'm working on a batch project that uses the spring batch core library
the library uses jdbcTemplate to persist jobs meta data
and i'm trying to use hibernate in order to read the data about a specific job
package com.ben.batch.repository;
import org.springframework.batch.core.JobInstance;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
public interface JobInstanceRepository extends JpaRepository<JobInstance,Long> {
#Query("select count(j) from JobInstance j where j.jobName in :jobName ") //Can't resolve symbol 'JobInstance'
Long countBuJobName(String jobName);
}
in ordinary spring boot project this works but now it shows this error
Can't resolve symbol 'JobInstance'
tho I imported the class correctly
Any idea would be much appreciated.

For similar purposes exists JobRepository
http://docs.spring.io/spring-batch/apidocs/org/springframework/batch/core/repository/JobRepository.html
It allows you to fetching any information regarding your jobs.

the spring batch infrastructure is not yet available as spring-data repository, see this JIRA Ticket BATCH-2203

JobInstance is not a Hibernate entity (source code for reference). You'll need to implement your own Hibernate persistence layer if you'd like to query the tables using Hibernate. The primary reason for this is that the framework allows you to define any table prefix you like so your tables would end up as BATCH_JOB_EXECUTION, NIGHTLY_JOB_EXECUTION, ABCD_JOB_EXECTION, etc. Because of that the Hibernate model wouldn't know what table names to point to.

Related

how to Resolve "could not initialize proxy - no session" error when using Spring repository

I'm working on a mutitenant project it maintains different schema for each tenant, followed Project
As we are dynamically switching the tenants so it looks like some configuration is missed which is closing the session or not keeping the session open to fetch the LAZY loaded objects. Which results in "could not initialize proxy - no session" error.
Please check below link to access the complete project and db schema scripts, please follow the steps given in Readme file.
Project
It will be helpful if someone can point out the issue in the code.
i tried to put service methods in #Transactional annotation but that didn't work.
I'm expecting it to make another call to the LAZY loaded object, This project is simplefied verson of the complex project, actually i have lot more lazy loaded objects.
Issue:-
I'm getting no Session error "could not initialize proxy [com.amran.dynamic.multitenant.tenant.entity.Tenant#1] - no Session"
at line 26 (/dynamicmultitenant/src/main/java/com/amran/dynamic/multitenant/tenant/service/ProductServiceImpl.java)
The issue is that your transaction boundaries are not correct. In TenantDatabaseConfig and MasterDatabaseConfig you've correctly added #EnableTransactionManagement, which will setup transactions when requested.
However - the outermost component that has an (implicit) #Transactional annotation is the ProductRepository (by virtue of it being implemented by the SimpleJpaRepository class - which has the annotation applied to it - https://github.com/spring-projects/spring-data-jpa/blob/864c7c454dac61eb602674c4123d84e63f23d766/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java#L95 )
and so your productRepository.findAll(); call will start a transaction, create a JPA session, run the query, close the session, close the transaction, which means that there is no longer any transaction / session open in which to perform the lazy-loading.
Therefore, your original attempt of
i tried to put service methods in #Transactional annotation but that didn't work.
IS the correct thing to do.
You don't say exactly what you tried to do, and where, but there are a few things that could have gone wrong. Firstly, make sure you're adding a org.springframework.transaction.annotation.Transactional and not a javax.transaction.Transactional annotation.
Secondly (and the more likely problem in this scenario), you'll need to configure the annotation with which transaction manager the transaction should be bound to, otherwise it may use an existing / new transaction created against the master DB connection, not the tenant one.
In this case, I think that:
#Service
#Transactional(transactionManager = "tenantTransactionManager")
public class ProductServiceImpl implements ProductService {
should work for you, and make all the methods of the service be bound to a transaction on the tenant DB connection.
EDIT: Answering a follow-up question:
can you please also suggest a better way to inject my tenantTransactionManager in all my service classes, as I don't want to mention tenantTxnManger in all service classes if there is any better way to do it ?
Yes, sure. You can create a meta-annotation that applies multiple other annotations, so you could create:
/**
* Marks class as being a service operating on a single Tenant
*/
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Service
#Transactional("tenantTransactionManager")
public #interface TenantService {
}
and then you can simply annotate your service classes with #TenantService instead of #Service:
#TenantService
public class ProductServiceImpl implements ProductService {

Spring boot with multiple database connections

I made a simple SpringBoot REST application for testing purposes where the tables are in 2 databases, one is Mysql and one is Postgresql. To configure the 2 connections I used the instructions from here, at point 6 - "Multiple Databases in Spring Boot" and all seemed to be fine, the 2 connections were initiated but only the primary connection works.
So if the Mysql connection is annotated as #Primay only Mysql REST services work, on Postgresql the error for all tables is "org.hibernate.hql.internal.ast.QuerySyntaxException: <Entity_Name> is not mapped". But if I make a single change and set #Primary on the Postgresql connection then all Postgres tables are working and all Mysql tables give the same error(table not mapped).
So somehow I think the right connection is not autoselected based on the package.
UPDATE: I found another tutorial here using different database types, I followed the instructions but the result is the same, all tables in the secondary database give the error "org.hibernate.hql.internal.ast.QuerySyntaxException: <Entity_Name> is not mapped". I think the secondary connection is not used, somehow the primary one defaults on the wrong tables but I don't know why.
I uploaded this small Github project with my work.
https://github.com/victorqedu/MultipleSpringBootDS
UPDATE: In the DAO class a have autowired the constructor and #Autowire is setting the wrong EntityManager(I think this is the source of the problem), could I manually specify the right EntityManager?
#Autowired
public AntibiogramaAntibioticeDAOHibernateImpl(EntityManager theEntityManager) {
entityManager = theEntityManager;
}
I also tried the annotation #PersistenceContext on the EntityManager but the result is the same.
#PersistenceContext
private EntityManager entityManager;
I'm not sure the problem is EntityManagaer or the Session that I obtain from EntityManager.unwrap, seems to be little documentation about this...
This can be solved with Qualifier in a short description if you have multiple same type of beans(like EntityManager) you should use qualifier to wire them.
Therefore in your code you should
public AntibiogramaAntibioticeDAOHibernateImpl(
#Qualifier("primaryEntityManagerFactory") EntityManager theEntityManager)

Spring Data MongoDB eliminate POJO's

My system is a dynamic telemetry system. We have hundreds of different spiders all sending telemetry back to the SpringBoot server, Everything is dynamic, driven by json files in Mongo, including the UI. We don't build the UI, as opposed to individual teams can configure their own UI for their needs, all by editing json docs.
We have the majority of the UI running and i began the middleware piece. We are using Spring Boot for the first time along with Spring Data Mongo with several MQ listeners for events. The problem is Spring Data. I started reading the docs on it and I realized the docs do not address using it without POJO's. I have this wonderfully dynamic model that changes per user per minute if the telemetry spiders dictate, I couldn't shackle this to a POJO if I tried. Is there a way to use Spring Data with a Map?
It seems from my experiments that the big issue is there is no way to tell the CRUD routines of the repository class what collection to query without a POJO.
Are my suspicions correct in that this won't work and am I better off ditching Spring Data and using the Mongo driver directly?
I don't think you can do it without a pojo when using spring-data. The least you could do is this
public interface NoPojoRepository extends MongoRepository<DummyPojo, String> {
}
and create a dummy pojo with just id and a Map.
#Data
public class DummyPojo {
#Id
private String id;
private Map<String, Object> value;
}
Since this value field is a map, you can store pretty much anything.

Auto insert default record when deploying Spring MVC App with Spring Security

I am looking for a way to auto insert a default admin account, using JPA, when my spring mvc application is deployed.
My database is generated based on the Entities.
I want to kick off something that will insert a default admin user, assign roles, every time the application is deployed.
It depends on which implementation of JPA you use.
If you use Hibernate you can add import.sql file (that contains records to load) to the class path. More info here.
As a workaround you can also use dbunit tool.
I would recommend having a migration utility that will keep your database in synch with your codebase - these are typically DDL's, but again the queries to insert default admin user, assign roles etc can also be part of this migration utility. There are very good one's available - Flyway is one that I have used, Liquibase is another one.
There is a very good comparison of the different migration utilities on the Flyway homepage also that you can look at.
i use CommandLineRunner interface.
#Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
#Autowired
UserRepository userRepository;
#Override
public void run(String...args) throws Exception {
User admin = new user(firstName);
userRepository.save(admin);
}
}
before the app starts this class will be executed.
you can find other ways here : Guide To Running Logic on Startup in Spring

Testing Hibernate Mappings

I'm using Hibernate to map objects to a legacy schema which contains some ginormous tables via annotations (as XML files are so 2003). Since these classes are so large, yes I occasionally make an occasional typo, which Hibernate doesn't bother to tell me about until I try to run it.
Here's what I've tried:
One: Setting hbm2ddl.auto to "validate":
This causes the String values of the class to validate against varchar(255). Since many of the column types in the database are CHAR(n), this blows up. I would have to add the columnDefinition="CHAR(n)" to several hundred mappings.
Two: Using Unitils.
Importing these via Maven causes imports of dependency libraries which blow up other sections of code. Example: I'm using Hibernate 4.1, but Unitils imported Hibernate 3.2.5 and blew up a UserType.
So, is there another way to do this? I looked at the Unitils code to see if I could simply yank the sections I needed (I do that with apache-commons fairly often when I just need a single method), but that's not a simple task.
Hibernate is configured via a Spring application context.
Any ideas out there?
I would write tests against an in-memory database (HSQLDB, H2) using the Spring testing framework. You'll quickly see any mapping errors when you attempt to run queries against the tables.
The test class would look something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=MyTestConfig.class)
#TransactionConfiguration(transactionManager="txMgr", defaultRollback=true)
public class MyTest {
#Autowired
private SessionFactory sessionFactory;
// class body...
}
I would configure Hibernate to auto-deploy the tables as part of the tests.

Resources