Is it possible to fetch data from database before server is getting started? - database

I am working on a Spring project. I want to use scheduler in it and want to schedule it on a variable date. This date has to be taken from database. Is it possible to fetch data from database before server is getting started?

Two solutions come to my mind:
#PostConstruct annotated method of some #Component:
#Component
public class MyBean
{
#PostConstruct
public void init()
{
// Blocking DB call could go here
}
}
Application Events. For the ApplicationReadyEvent:
#Component
public class ApplicationReadyEventListener implements ApplicationListener<ApplicationReadyEvent>
{
#Override
public void onApplicationEvent(ApplicationReadyEvent event)
{
// DB call could go here
//
// Notice that if this is a web services application, it
// would potentially be serving requests while this method
// is being executed
}
}

Related

Spring Boot REST: How to dynamically access the appropriate database schema specified in a client request?

I have one database with 3 schemas (OPS, TEST, TRAIN). All of these schemas have a completely identical table structure. Now lets say I have an endpoint /cars that accepts a query param for the schema/environment. When the user makes a GET request to this endpoint, I need the Spring Boot backend to be able to dynamically access either the OPS, TEST, or TRAIN schema based on the query param specified in the client request.
The idea is something like this where the environment is passed as a request param to the endpoint and then is somehow used in the code to set the schema/datasource that the repository will use.
#Autowired
private CarsRepository carsRepository;
#GetMapping("/cars")
public List<Car> getCars(#RequestParam String env) {
setSchema(env);
return carsRepository.findAll();
}
private setSchema(String env) {
// Do something here to set the schema that the CarsRepository
// will use when it runs the .findAll() method.
}
So, if a client made a GET request to the /cars endpoint with the env request param set to "OPS" then the response would be a list of all the cars in the OPS schema. If a client made the same request but with the env request param set to "TEST", then the response would be all the cars in the TEST schema.
An example of my datasource configuration is below. This one is for the OPS schema. The other schemas are done in the same fashion, but without the #Primary annotation above the beans.
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "opsEntityManagerFactory",
transactionManagerRef = "opsTransactionManager",
basePackages = { "com.example.repo" }
)
public class OpsDbConfig {
#Autowired
private Environment env;
#Primary
#Bean(name = "opsDataSource")
#ConfigurationProperties(prefix = "db-ops.datasource")
public DataSource dataSource() {
return DataSourceBuilder
.create()
.url(env.getProperty("db-ops.datasource.url"))
.driverClassName(env.getProperty("db-ops.database.driverClassName"))
.username(env.getProperty("db-ops.database.username"))
.password(env.getProperty("db-ops.database.password"))
.build();
}
#Primary
#Bean(name = "opsEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean opsEntityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("opsDataSource") DataSource dataSource
) {
return builder
.dataSource(dataSource)
.packages("com.example.domain")
.persistenceUnit("ops")
.build();
}
#Primary
#Bean(name = "opsTransactionManager")
public PlatformTransactionManager opsTransactionManager(
#Qualifier("opsEntityManagerFactory") EntityManagerFactory opsEntityManagerFactory
) {
return new JpaTransactionManager(opsEntityManagerFactory);
}
}
Personally, I don't feel its right to pass environment as Request Param and toggle the repository based on the value passed.
Instead you can deploy multiple instance of the service pointing to different data source and have a gate keeper(router) to route to the respective service.
By this way clients will be exposed to one gateway service which in turn routes to respective service based on input to gate keeper.
You typically don't want TEST/ACPT instances running on the very same machines because it typically gets harder to [keep under] control the extent to which load on these environments will make the PROD environment slow down.
You also don't want the setup you envisage because it makes it nigh impossible to evolve the app and/or its database structure. (You're not going to switch db schema in PROD at the very same time you're doing this in DEV are you ? Not doing that simultaneous switch is wise, but it breaks your presupposition that "all three databases have exactly the same schema".

Spring Data MongoDB #Transactional failure

Could someone please tell me why this spring transaction is not rolling back appropriately?
The error I get is this:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.transaction.PlatformTransactionManager' available
This is my repository with a save transaction that will intentionally fail:
#Repository
public class TransactionalRepository {
private final PlayerRepository playerRepository;
#Autowired
public TransactionalRepository(PlayerRepository playerRepository) {
this.playerRepository = playerRepository;
}
public Player saveSuccess(Player player) {
return playerRepository.save(player);
}
#Transactional
public Player saveFail(Player player) {
player.setName("FAIL"); // should not be saved in DB if transaction rollback is successful
player = playerRepository.save(player);
throw new IllegalStateException("intentionally fail transaction");
}
}
And here is the test:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MongoTransactionApplicationTests {
#Autowired
public TransactionalRepository playerRepository;
#Test
public void contextLoads() {
Player player = new Player();
player.setId(UUID.randomUUID().toString());
final String PLAYER_NAME = "new-"+player.getId().subSequence(0,8);
player.setName(PLAYER_NAME);
player = playerRepository.saveSuccess(player);
try {
player = playerRepository.saveFail(player);
} catch (IllegalStateException e) {
// this is supposed to fail
}
Assert.assertEquals(PLAYER_NAME, player.getName());
}
}
Download all the code here if you want to see it run
Unlike other implementations the Spring Data MongoDB module does not by default register a PlatformTransactionManager if none is present. This is up to the users configuration, to avoid errors with non MongoDB 4.x servers as well as projects already using #Transactional along with a non MongoDB specific transaction manager implementation. Please refer to the reference documentation for details.
Just add a MongoTransactionManager to your configuration.
#Bean
MongoTransactionManager txManager(MongoDbFactory dbFactory) {
return new MongoTransactionManager(dbFactory);
}
You might also want to check out the Spring Data Examples and have a look at the one for MongoDB transactions.

Create Interceptor to calculate execution time of each Web APIs in C#

I am working on AngularJS, WebApi. Have angular services to fetch data from database using Entity-Framework LINQ.
So, when I run my app, I want to see how much time my web api took to return response from server?
How can we do it?
There are several ways to accomplish this thing.
First way is to use ActinoFilter. For that refer this answer.
Second way is to use Middleware
Add new class file, here it's LoggingMiddleware
public class LoggingMiddleware
{
private readonly RequestDelegate _next;
LoggingDataAccess _loggingDataAccess; // your data access
readonly Stopwatch _stopwatch = new Stopwatch();
public LoggingMiddleware(RequestDelegate next, LoggingDataAccess loggingDataAccess)
{
_next = next;
_loggingDataAccess = loggingDataAccess;
}
public async Task Invoke(HttpContext context)
{
// start request time
_stopwatch.Start();
await _next.Invoke(context);
// end request time and get elapsed milliseconds
_stopwatch.Stop();
// save time to your table
var time= _stopwatch.ElapsedMilliseconds;
//reset stopwatch
_stopwatch.Reset();
}
}
Add your Middleware in Startup.cs (ASP.NET Core)
public void Configure(IApplicationBuilder app...)
{
app.UseMiddleware<LoggingMiddleware>();
}
Hope this helps!

Should new instance created for each request?

I am building using java servlet/jsp. I have a class to handle database connection, but I dont know should I create each instance for each request or one instance for all requests.
For instance:
Scenario 1:
class HandleDB {
public static HandleDB getInstance(); // singleton pattern
public void initConnection();
public void releaseConnection();
}
then,
//at the beginning of a request:
HandleDB.getInstance().initConnection();
// handle tasks
// at the end of request
HandleDB.getInstance().releaseConnection();
Scenario 2:
class HandleDB {
public void initConnection();
public void releaseConnection();
}
//at the beginning of a request:
HandleDB db = new HandleDB();
db.initConnection();
// handle tasks
// at the end of request
db.releaseConnection();
db = null;
Which scenario should be used in practice?
Go with Scenario 2. The problem with Scenario 1 is that the same HandleDB instance will be shared by all requests and could lead to thread safety issues. Keep in mind that requests can be executed in parallel. The standard is to have one connection per thread/request.
Most Web applications use a connection pool (like C3P0 or Apache DBCP) to avoid having to create a new connection for each request. You get a connection from the pool at the beginning of the request and return it to the pool at the end of the request, so other requests can reuse it later.
Use Listeners LINK
public class AppServletContextListener implements ServletContextListener{
#Override
public void contextDestroyed(ServletContextEvent arg0) {
/// Destroy DB Connection
}
#Override
public void contextInitialized(ServletContextEvent arg0) {
/// Create DB Connection
}
}
if you have batch of tasks you should create database connection only at beginning of first task then after finishing all task you should release or free db connection
for your case scenario 1 is applicable.

Initialize Spring embedded database after deployment

I have an Spring MVC app with an embedded database (HSQLDB) that I want to initialize after deployment. I know that I could use an xml script to define initial data for my datasource but, as long I'm using JPA + Hibernate, I would like to use Java code. Is there a way to do this?
Heavily updated answer (it was too complex before):
All you need is to add initializing bean to your context, which will insert all the necessary data into the database:
public class MockDataPopulator {
private static boolean populated = false;
#Autowired
private SessionFactory sessionFactory;
#PostConstruct
public void populateDatabase() {
// Prevent duplicate initialization as HSQL is also initialized only once. Duplicate executions
// can happen when the application context is reloaded - e.g. when running unit tests).
if (populated) {
return;
}
// Create new persistence session
Session session = sessionFactory.openSession();
session.setFlushMode(FlushMode.ALWAYS);
// Insert mock entities
session.merge(MockDataFactory.createMyFirstObject())
session.merge(MockDataFactory.createMySeconfObject())
// ...
// Flush and close
session.flush();
session.close();
// Set initialization flag
populated = true;
}
}

Resources