I've a job with one Step. The step has the usual setup.
reader: read from a Stock DB-table "instrument"
processor: retrieving the latest price from an external service
writer: writing the latest price in the price DB-table "instrumentprice"
There is a ItemWriteListener which does some calculations on the latest price in db-table "instrumentprice". So, it's important that the writer is persisting the latest price immediately in the DB-table "instrumentprice"
In the writer the methods, persist and flush are called on the EntityManager for the Entity "instrumentprice".
But Hibernate doesn't write into the table "instrumentprice" immediately. It does write into the table "instrumentprice" at a later stage, but I haven't figured out the mechanism yet.
#Configuration
#ComponentScan(basePackages = {"com.chartinvestbatch.alphaVantageHistory"})
public class JobConfig {
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private ChartInvestJobListener chartInvestJobListener;
#Bean
public Job jobAlphaVantage(#Qualifier("stepProcessingPrices") Step stepProcessingPrices) throws IOException {
return jobBuilderFactory
.get("JobAlphaVantage")
.listener(chartInvestJobListener)
.incrementer(new RunIdIncrementer())
.start(stepProcessingPrices)
.build();
}
}
#Configuration
public class StepConfig {
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Autowired
private TaCalcListener taCalcListener;
#Bean
#Qualifier("stepProcessingPrices")
public Step stepProcessingPrices(HibernateCursorItemReader<Instrument> hibernateCursorItemReader, ItemProcessor<Instrument, InstrumentAndPricesDto> itemProcessor, ItemWriter<InstrumentAndPricesDto> itemWriter) throws IOException {
return stepBuilderFactory
.get("stepProcessingPrices")
.<Instrument, InstrumentAndPricesDto>chunk(1)
.listener((ItemWriteListener<InstrumentAndPricesDto>) taCalcListener)
.reader(hibernateCursorItemReader)
.processor(itemProcessor)
.writer(itemWriter)
.build();
}
}
#Scope(value = "step")
#Component
#Transactional
public class StockItemWriter implements ItemWriter<InstrumentAndPricesDto> {
static Logger log = LogManager.getLogger(StockItemWriter.class);
#Autowired
private IntrumentPriceDao intrumentPriceDao;
#Override
public void write(List<? extends InstrumentAndPricesDto> instrumentAndPricesDtoList) throws Exception {
for (InstrumentAndPricesDto dto : instrumentAndPricesDtoList) {
// check some stuff etc ....
InstrumentPrice instrumentPrice = new InstrumentPrice();
instrumentPrice.setDate(dto.getDate());
...
intrumentPriceDao.persist(instrumentPrice);
intrumentPriceDao.flush();
}
}
}
#Transactional(propagation = Propagation.MANDATORY)
public abstract class GenericDao<T> {
#PersistenceContext
protected EntityManager entityManager;
public EntityManager getEntityManager() {
return entityManager;
}
public T persist(final T t) {
entityManager.persist(t);
return t;
}
public void flush() {
entityManager.flush();
}
}
#Repository
#Transactional(propagation = Propagation.MANDATORY)
public class IntrumentPriceDao extends GenericDao<InstrumentPrice> {
}
Related
It is my first user of spring batch. I want to get in every hour the stock of a product that designed by reference and id as List<Map<Date,Long>> to draw the graph of evolution of this product using angular google charts this is my service method
#Override
public Long getStockByRef(String ref, Long id) {
Iterable<Produit> MyProducts = storeService.getAllProductOfStore(id);
Long stock = 0L;
for (Produit produit : MyProducts) {
if(produit.getReference().equals(ref)) {
stock = produit.getStock();
}
}
return stock;
}
This is My RestController
#GetMapping("products/{id}/stock/{ref}")
public Long getStockByRef(#PathVariable Long id, #PathVariable String ref) {
Long stock = produitService.getStockByRef(ref, id);
return stock;
}
My Spring batch class config
#Configuration
#EnableBatchProcessing
public class SpringBatchConfig {
#Autowired private ProduitService produitService;
#Autowired private JobBuilderFactory jobBuilderFactory;
#Autowired private StepBuilderFactory stepBuilderFactory;
#Autowired private ItemReader<Long> itemStockReader;
#Autowired private ItemWriter<List<Map<Date,Long>>> itemStockWriter;
#Autowired private ItemProcessor<Long, List<Map<Date,Long>> > itemStockProcessor;
#Bean
public Job myJob() {
Step step1 = stepBuilderFactory.get("step-get-stock")
.<Long,List<Map<Date,Long>>>chunk(1)
.reader(itemStockReader)
.processor(itemStockProcessor)
.writer(itemStockWriter)
.build();
return jobBuilderFactory.get("data").start(step1).build();
}
}
How can I write My ItemReader ItemProcessor and my Itemwrite to return List<Map<Date,Long>> and send it via a #GetMapping?
I am learning spring transaction propagation. so i have three inner services in Organization Service.
OrganizationService
#Service
public class OrganizationServiceImpl implements OrganizationService {
#Autowired
EmployeeService employeeService;
#Autowired
HealthIsuranceService healthInsuranceService;
#Autowired
InsuranceLogService insuranceLogService;
#Override
#Transactional(propagation = Propagation.REQUIRED)
public void joinOrganization(Employee employee, EmployeeHealthInsurance employeeHealthInsurance) {
employeeService.insertEmployee(employee);
healthInsuranceService.registerEmployeeHealthInsurance(employeeHealthInsurance);
InsuranceLog insuranceLog = new InsuranceLog();
insuranceLog.setInformation("new log information");
insuranceLogService.logIt(insuranceLog);
}
#Override
public void leaveOrganization(Employee employee, EmployeeHealthInsurance employeeHealthInsurance) {
employeeService.deleteEmployeeById(employee.getEmpId());
healthInsuranceService.deleteEmployeeHealthInsuranceById(employeeHealthInsurance.getEmpId());
}
}
HealthInsuranceService
#Service
public class HealthInsuranceServiceImpl implements HealthIsuranceService{
#Autowired
HealthInsuranceDao healthInsuranceDao;
#Override
#Transactional(propagation = Propagation.REQUIRED)
public void registerEmployeeHealthInsurance(EmployeeHealthInsurance employeeHealthInsurance) {
healthInsuranceDao.registerEmployeeHealthInsurance(employeeHealthInsurance);
}
#Override
public void deleteEmployeeHealthInsuranceById(String empid) {
healthInsuranceDao.deleteEmployeeHealthInsuranceById(empid);
}
}
EmployeeService
#Service
public class EmployeeServiceImpl implements EmployeeService {
#Autowired
EmployeeDao employeeDao;
#Override
#Transactional(propagation = Propagation.REQUIRED)
public void insertEmployee(Employee employee) {
employeeDao.insertEmployee(employee);
}
#Override
public void deleteEmployeeById(String empid) {
employeeDao.deleteEmployeeById(empid);
}
}
EmployeeLogService
#Service
public class InsuranceLogServiceImpl implements InsuranceLogService{
#Autowired
private InsuranceLogDao insuranceLogDao;
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void logIt(InsuranceLog logInfo){
int i=0;
int j =0;
int result = i/j;
insuranceLogDao.logIt(logInfo);
}
}
here is my scenario :-
insertEmployee(employee) and registerEmployeeHealthInsurance(employeeHealthInsurance) should run in the same transaction so that either of them commits together or rolls back together. logIt(insuranceLog) is optional when these two transactions commits. failing to log should not roll back above transactions.
logIt(insuranceLog) should all also roll back when either of insertEmployee(employee) , registerEmployeeHealthInsurance(employeeHealthInsurance) fails to commit.
how can i achieve this ?
I am creating simple spring boot application. I used spring transaction management to handle the transaction. Here is my code.
ServiceImpl class
#Service("orderService")
public class OrderServiceImpl implements OrderService {
#Autowired
private CustomerDao customerDao;
#Autowired
private OrderDao orderDao;
#Transactional(rollbackFor = Exception.class)
#Override
public Long placeOrder(OrderPlacementRequest request) {
customerDao.save(request.getCustomer());
return orderDao.placeOrder(request.getOrder());
}
}
OrderDaoImpl class,
#Repository("orderDao")
public class OrderDaoImpl extends AbstractHibernateDao implements OrderDao {
#Override
public Long placeOrder(Order order) {
throw new RuntimeException("Test Error Message");
}
}
Configuration class,
#Configuration
#EnableTransactionManagement
public class HibernateConfig {
//Other Configurations
#Autowired
private final Environment environment;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(environment.getRequiredProperty("spring.datasource.driver-class-name"));
dataSource.setUrl(environment.getRequiredProperty("spring.datasource.url"));
dataSource.setUsername(environment.getRequiredProperty("spring.datasource.username"));
dataSource.setPassword(environment.getRequiredProperty("spring.datasource.password"));
return dataSource;
}
#Bean
public DataSourceTransactionManager transactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource());
return transactionManager;
}
}
application.properties
spring.aop.proxy-target-class=true
OrderController class
#RestController
#RequestMapping("/order")
public class OrderController {
#Autowired
private OrderService orderService;
#RequestMapping(value = "/place", method = RequestMethod.POST)
public Long placeOrder(#RequestBody OrderPlacementRequest request) {
return orderService.placeOrder(request);
}
}
In my case even though second method placeOrder failed. Customer will be saved in mysql database. What I wanted is to rollback the customer saving method. I went through few articles on transaction management including spring docs and stackoverflow. Still can not find the problem.
Updated with Controller class.
the problem is once i use method findAll using MongoOperation interface, it working fine with no exception but only return One Record, however the collection contain more than 1 record. i tried a different interface like mongotemp and also the same result, List size is return always 1, even when i try it from dao itself
Configuration Spring DATA with mongodb
#Configuration
public class MongodbConfig {
public #Bean MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(new MongoClient(), "inSpace");
}
public #Bean MongoTemplate mongoTemplate() throws Exception {
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory());
return mongoTemplate;
}
}
DAO Class
#Repository
public class GenericDAOImpl<T> implements GenericDAO<T> {
#Autowired
protected MongoOperations mongoOper;
public <T> void save(T ct) {
mongoOper.save(ct);
}
public <T> void remove(T ct) {
mongoOper.remove(ct);
}
public T getBydId(String id, Class<T> clazz) {
return mongoOper.findById(id, clazz);
}
public List<T> getAll(Class<T> clazz) {
System.err.println(" ''''''' " + mongoOper.findAll(clazz).size());
return mongoOper.findAll(clazz);
}
}
Note: the upper two classs in a single project and used as dependency for the other project who contain the below classes
Service calling DAO
#Service("userService")
public class UserServiceImpl implements UserService {
#Autowired
private GenericDAO<Users> userDAO;
public Users create(Users user) {
userDAO.save(user);
return user;
}
public Users delete(String id) {
Users user = userDAO.getBydId(id, Users.class);
userDAO.remove(user);
return user;
}
public List<Users> findAll() {
System.out.println("inside User Service");
return userDAO.getAll(Users.class);
}
public Users findById(String id) {
return userDAO.getBydId(id , Users.class);
}
}
Controller Class
#RestController
#RequestMapping("/user")
public class UsersController {
#Autowired
UserService userService;
#RequestMapping(method = RequestMethod.GET, value = "/allUsers")
List<Users> getAllRegistedUsers() {
System.out.println("inside UserController");
return userService.findAll();
}
}
Configuration Class
#SpringBootApplication
#ComponentScan(basePackageClasses=
{UsersController.class,UserService.class,GenericDAO.class})
public class UsersConfiguration {
public static void main(String[] args) {
SpringApplication.run(UsersConfiguration.class, args);
}
}
Note: when i try to inject bean in Main methods and use the bean by ApplicationContext it's work fine and return all records in database !?
I've been beating my head against a wall for awhile now trying to get this to work. I have created the following data access object:
public interface GenericDAO<T, ID extends Serializable> {
T findById(ID id);
List<T> findAll();
T save(T entity);
void update(T entity);
void delete(T entity);
}
public class GenericHibernateDAO<T, ID extends Serializable> implements GenericDAO<T, ID> {
private final Class<T> persistentClass;
private final SessionFactory sessionFactory;
public GenericHibernateDAO(final SessionFactory sessionFactory) {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
this.sessionFactory = sessionFactory;
}
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
public Class<T> getPersistentClass() {
return persistentClass;
}
#Override
public T findById(final ID id) {
return (T) getSession().load(getPersistentClass(), id);
}
#Override #SuppressWarnings("unchecked")
public List<T> findAll() {
return findByCriteria();
}
protected List<T> findByCriteria(final Criterion... criterion) {
final Criteria crit = getSession().createCriteria(getPersistentClass());
for (final Criterion c : criterion) {
crit.add(c);
}
return crit.list();
}
#Override
public T save(final T entity) {
getSession().saveOrUpdate(entity);
return entity;
}
#Override
public void delete(final T entity) {
getSession().delete(entity);
}
#Override
public void update(final T entity) {
getSession().saveOrUpdate(entity);
}
}
#Repository
public class StockHibernateDAO extends GenericHibernateDAO<Stock, String> implements StockDAO {
#Inject
public StockHibernateDAO(final SessionFactory sessionFactory) {
super(sessionFactory);
}
}
I'm attempting to set this up with Java Configuration, so here is my configuration to setup my service layer:
#Configuration #Profile("hibernate")
#EnableCaching #EnableTransactionManagement
#ComponentScan("reference.dao.hibernate")
public class HibernateServiceConfig implements TransactionManagementConfigurer {
#Inject private StockDAO stockDao; //No extra methods, just the base stuff for now
#Bean(destroyMethod = "shutdown")
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).addScript("classpath:schema.sql").build();
}
#Bean
public SessionFactory sessionFactory() {
return new LocalSessionFactoryBuilder(dataSource()).addAnnotatedClasses(Stock.class)
.setProperty("hibernate.show_sql", "true")
.setProperty("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.EhCacheRegionFactory")
.setProperty("hibernate.cache.use_query_cache", "true")
.setProperty("hibernate.cache.use_second_level_cache", "true")
.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect").buildSessionFactory();
}
#Override #Bean
public PlatformTransactionManager annotationDrivenTransactionManager() {
return new HibernateTransactionManager(sessionFactory());
}
}
Here is the TradingService:
#Service
public class TradingServiceImpl implements TradingService {
#Inject private StockDAO stockDAO;
#Override #Transactional
#CachePut(value = "stockCache", key = "#stock.name")
public Stock addNewStock(final Stock stock) {
stockDAO.save(stock);
return stock;
}
#Override #Cacheable(value = "stockCache")
public Stock getStock(final String stockName) {
return stockDAO.findById(stockName);
}
#Override #CacheEvict(value = "stockCache", key = "#stock.name")
public void removeStock(final Stock stock) {
stockDAO.delete(stock);
}
#Override #CacheEvict(value = "stockCache", key = "#stock.name")
public void updateStock(final Stock stock) {
stockDAO.update(stock);
}
#Override
public List<Stock> getAll() {
return stockDAO.findAll();
}
}
The saving of a stock only seems to be completed if I add a session.flush() to the save method. The way I understand things, having the TransactionManager and the #Transactional around the service layer method should in fact cause that call to be made for me. What is this configuration missing?
Because you are injecting a Session
#Bean
public Session session() {
return sessionFactory().openSession();
}
Spring cannot add it's transactional behavior around it. Let Spring open the session and do it's business.
Instead of injecting a Session, inject a SessionFactory. In your DAO, keep a attribute for SessionFactory and use sessionFactory.getCurrentSession() to acquire a session.
When Spring sees the #Transactional, it will get the SessionFactory, call openSession(), begin a transaction on it, then call your method. When your method returns successfully, it will close that transaction.
You should also probably #Autowired the dao in your service class.