Reading value from Service method in spring batch - java

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?

Related

Spring/Hibernate Multitenancy - LazyInitializationException: could not initialize proxy error

I'm building a multitenant application and used this as a source of reference: https://www.baeldung.com/multitenancy-with-spring-data-jpa. Although most of the DB calls work fine, some are exhibiting confusing behavior. Here is an example:
The following class MultiTenantConfigurationprovides all the necessary beans for configuring the datasource, sessions and transactions:
class MultiTenantConfiguration {
#Bean
public DataSource dataSource() {
AbstractRoutingDataSource dataSource = new RoutingDataSource();
dataSource.setTargetDataSources(....);
dataSource.setDefaultTargetDataSource(....);
dataSource.afterPropertiesSet();
return dataSource;
}
#Bean
public SessionFactory entityManagerFactory(DataSource dataSource) {
LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(dataSource);
builder.scanPackages("....");
return builder.buildSessionFactory();
}
#Bean
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
}
The following class provides the id of DB source to lookup:
class RoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return TENANT_CONTEXT.getCurrentTenant();
}
}
This is the API service which calls the internal user service:
class UserAPIResource {
#GET
#Path("/api/users_0/{id}")
#Produces(MediaType.APPLICATION_JSON)
public User getUser0(#PathParam("id") Long id) {
return userService.getUserForId_0(id);
}
#GET
#Path("/api/users_1/{id}")
#Produces(MediaType.APPLICATION_JSON)
public User getUser1(#PathParam("id") Long id) {
return userService.getUserForId_1(id);
}
#GET
#Path("/api/users_2/{id}")
#Produces(MediaType.APPLICATION_JSON)
public User getUser2(#PathParam("id") Long id) {
return userService.getUserForId_2(id);
}
}
This is the UserService that communicates with the JPA repo - UserRepository, converts it into a consumable POJO and returns:
class UserService {
#Autowired
UserRepository userRepo;
public User getUserForId_0(Long id) {
RawUser rawUser= userRepo.getById(id);
User user = new User();
user.setId(rawUser.getId());
user.setName(rawUser.getName());
return user;
}
#Transactional
public User getUserForId_1(Long id) {
RawUser rawUser= userRepo.getById(id);
User user = new User();
user.setId(rawUser.getId());
user.setName(rawUser.getName());
return user;
}
public User getUserForId_2(Long id) {
RawUser rawUser= userRepo.findById(id);
User user = new User();
user.setId(rawUser.getId());
user.setName(rawUser.getName());
return user;
}
}
Here is the RawUser entity class. It does not have any mappings to other entities:
#Entity
#EntityListeners(AuditingEntityListener.class)
class RawUser {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
#Column(name = "name")
private String name;
public long getId() {return id;}
public String getName() {return name;}
}
Now, /api/users_0/1 throws: org.hibernate.LazyInitializationException: could not initialize proxy [com.db.model.User#1] - no Session.
However, /api/users_1/1 and /api/users_2/1 return successfully.
The difference is that /api/users_1/1 which calls userService.getUserForId_1() is marked as Transactional.
And /api/users_2/1 which calls userService.getUserForId_1() uses findById() instead of getById().
This behavior only happens in the multi-tenant structure. So I'm guessing I'm missing some additional configuration, specifically in Sessions/Proxy? If someone can explain this, that'd be great.

Spring Batch with Hiberbate , Entity not persisted after flush

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> {
}

How to update the data in Spring Boot MVC

I have the most common project on Spring Boot MVC. and I'm trying to write update data via PUT.
#RestController
#RequestMapping(CommentController.PATH)
public class CommentController {
public final static String PATH = "/comments";
#Autowired
private CommentService service;
#PutMapping("/{id}")
public Comment update(#RequestBody Comment comment, #PathVariable Long id) {
return service.update(id, comment);
}
}
#Service
public class CommentService {
#Autowired
private CommentRepository repository;
public Comment update(Long id, Comment entity) {
Optional<Comment> optionalEntityFromDB = repository.findById(id);
return optionalEntityFromDB
.map(e -> saveAndReturnSavedEntity(entity, e))
.orElseThrow(getNotFoundExceptionSupplier("Cannot update - not exist entity by id: " + id, OBJECT_NOT_FOUND));
}
private Comment saveAndReturnSavedEntity(Comment entity, Comment entityFromDB) {
entity.setId(entityFromDB.getId());
return repository.save(entity);
}
}
#Repository
public interface CommentRepository extends JpaRepository<Comment, Long> {
}
#Entity
public class Comment {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
#Column(name = "name")
protected String name;
}
then I write a test with the ability to check for updated data:
#SpringBootTest
#RunWith(SpringRunner.class)
#Transactional
// DBUnit config:
#DatabaseSetup("/comment.xml")
#TestExecutionListeners({
TransactionalTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DbUnitTestExecutionListener.class
})
public class CommentControllerTest {
private MockMvc mockMvc;
private static String route = PATH + "/{id}";
#Autowired
private CommentController commentController;
#Autowired
private CommentRepository commentRepository;
#PersistenceContext
private EntityManager entityManager;
#Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(commentController)
.build();
}
#Test
public void update_ShouldReturnCreated2() throws Exception {
int id = 1;
String name = "JohnNew";
Comment expectedComment = new Comment();
expectedComment.setName(name);
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = ow.writeValueAsString(expectedComment);
this.mockMvc.perform(put(route, id)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(json))
.andDo(print());
entityManager.clear();
entityManager.flush();
Comment commentUpdated = commentRepository.findById(1L).get();
assertThat(commentUpdated.getName(), equalTo(name)); // not equals!
}
}
comment.xml:
<dataset>
<Comment id="1" name="John" />
</dataset>
but the problem is that the data is not updated.
If you enable the logging of Hibernat, then there is also no update request to the database.
What am I doing wrong?
You are missing off the #Transactional annotation from your CommentService. Whilst it can be better to add it at the per-method level, try adding it to class level to verify this fixes things:
#Service
#Transactional
public class CommentService {

how to import data from H2 database to elasticsearch?

I am trying to search data from H2 database using elasticsearch. elasticsearch work fine in my project.I added elasticsearch externally.when i add data using constructor it adds to elasticsearch.but if try to add data from H2 database to elasticsearch I am getting this error.
-Error creating bean with name 'itemMigrator' defined in file.
-Error creating bean with name 'userJpaRepository': Invocation of init method failed;
-No property findAll found for type User!
spring-boot-starter-parent 2.0.6.RELEASE
elasticsearch version-5.6.12
I am following this example https://www.hameister.org/SpringBootElasticsearch.html
http://techprimers.com/spring-data-elastic-search-example-3-using-spring-jpa/
User.java
#Entity
#Document(indexName = "user", type = "user", shards = 1)
#Table(name = "user_detail")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#NotBlank
#Column(name = "first_name")
private String firstName;
#NotBlank
#Column(name = "last_name")
private String lastName;
SearchQueryBuilder.java
#Component
public class SearchQueryBuilder {
private ElasticsearchTemplate elasticsearchTemplate;
#Autowired
public SearchQueryBuilder(ElasticsearchTemplate elasticsearchTemplate) {
this.elasticsearchTemplate = elasticsearchTemplate;
}
public List<User> getAll(String text) {
QueryBuilder query = QueryBuilders.boolQuery()
.should(
QueryBuilders.queryStringQuery(text)
.lenient(true)
.field("firstName")
.field("role")
).should(QueryBuilders.queryStringQuery("*" + text + "*")
.lenient(true)
.field("firstName")
.field("role"));
NativeSearchQuery build = new NativeSearchQueryBuilder()
.withQuery(query)
.build();
List<User> userses = elasticsearchTemplate.queryForList(build, User.class);
return userses;
}
}
ItemMigrator.java
#Autowired
ElasticsearchOperations operations;
#Autowired
UsersRepository UsersRepository;
#Autowired
UserJpaRepository jpaRepository;
#Autowired
public ItemMigrator(UserJpaRepository jpaRepository, ElasticsearchTemplate operations, UsersRepository UsersRepository) {
this.jpaRepository = jpaRepository;
this.operations = operations;
this.UsersRepository = UsersRepository;
}
#PostConstruct
#Transactional
public void loadAll() {
Iterable<User> items = jpaRepository.findAll();
operations.putMapping(User.class);
UsersRepository.saveAll(items);
}
}
UserJpaRepository.java
public interface UserJpaRepository extends JpaRepository<User, Long>
UsersRepository.java
public interface UsersRepository extends ElasticsearchRepository<User, Long>{
List<User> findByFirstName(String text);
List<User> findByRole(String text);
ManualSearchResource.java
#RestController
#RequestMapping("/rest/manual/search")
public class ManualSearchResource {
#Autowired
private SearchQueryBuilder searchQueryBuilder;
#GetMapping(value = "/{text}")
public List<User> getAll(#PathVariable final String text) {
return searchQueryBuilder.getAll(text);
}
}
Config.java
#Configuration
#EnableJpaRepositories(basePackages = "net.kzn.shoppingbackend")
#EnableElasticsearchRepositories(basePackages = "net.kzn.shoppingbackend")
#ComponentScan(basePackages = { "net.kzn.shoppingbackend" })
public class Config {
#Bean
public Client client() throws UnknownHostException {
Settings settings = Settings.builder()
.put("client.transport.sniff", true)
.put("cluster.name", "elasticsearch").build();
#SuppressWarnings("resource")
TransportClient client = new PreBuiltTransportClient(settings)
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
return client;
}
#Bean
public ElasticsearchOperations elasticsearchTemplate() throws UnknownHostException {
return new ElasticsearchTemplate(client());
}
}
This is my stacktrace https://www.dropbox.com/s/nxf2a3m961dx7a7/elastic_error.txt?dl=0
Is it correct to add both #Entity and #Document is same class?Please tell me what am i doing wrong here.

#Transactional not rolling back with RuntimeException

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.

Categories