I am new to the spring batch and I have an application where for example an xml-file of flights is read and saved to a database. The whole process is already working, but due to a certain use case I also need to access the data inside the reader object (ItemReader), should it is possible.
Down there is the reader-method. It is not about this method in particular, but as mentioned it is about ItemReader.
#Bean
public StaxEventItemReader<Flight> flightReader() {
StaxEventItemReader<Flight> reader = new StaxEventItemReader<Obj>();
reader.setResource(ressource);
reader.setFragmentRootElementName("flight");
Map<String, String> aliases = new HashMap<String, String>();
aliases.put("flight", Flight.class);
XStreamMarshaller xStreamMarshaller = new XStreamMarshaller();
xStreamMarshaller.setAliases(aliases);
reader.setUnmarshaller(xStreamMarshaller);
return reader;
}
How can I access the flight objects inside the reader (StaxEventItemReader) object?
I actually tried to use the read() method (Spring doc ItemReader), but I am always getting NullPointerExceptions.
If the read() method is the correct way, how can you access the flight objects inside ItemReader correctly?
If not, are there other ways?
There is more than one way to access the items. It really depends on what you want to do with them:
If you only want to have a look without manipulating the items, you can implement an ItemReadListener with its afterRead method, and add the listener to your step.
The items are passed to the processor. So you can operate on them there.
You can extend the class StaxEventItemReader and override the read method to include additional logic.
If you prefer composition over inheritance, you can write a new reader that uses a StaxEventItemReader as a delegate.
Related
I'm using opencsv for read/write csv files using opencsv annotations.
My bean is having fields not just primitives, but a java HashMap as well.
Now what i want to do is
public class MyBean {
#CsvBindByName(column = "ID")
private int id;
#CsvBindByName(column = "PROPERTIES")
private Map<String, String> sampleMap = new HashMap<String, String>();
TO
ID, property1, property2...
1, value1, value2.....
I'd like to get this working in both read/write.
as i understand, the default MappingStrategy doesn't work in this case. Also Creating Custom MappingStrategy doesn't makes sense for HashMap field. because we don't know the complete field list until we iterate all the map.
Another way to get column names is that just read one bean from the list of beans. And get access to HashMap then create the header.(Hashmap keys are fixed across beans in my case)
MappingStrategy only concerned about Class level meta data. Like fields etc.
public static Field[] getAllFields(Class<?> cls) {
List allFieldsList = getAllFieldsList(cls);
return (Field[])allFieldsList.toArray(new Field[allFieldsList.size()]);
}
getting access to the real data for creating csv header doesn't look like a natural way to do.
Any advice on how to solve this?
Please point me out to any other libraries out there that can do read/write into beans having Map field.
Cheers!
Sadly openCSV does not support this. A quick google showed me that SuperCSV comes close but it puts everything in a map whereas you want to control what goes in the map and what does not. There may be others out there but most require a one to one between the fields in the object and the columns in the csv file.
This was something that I wanted to develop for years and contribute because the company I currently work for has need for that but they do not want to pay me to develop it and I have higher priorities for openCSV when free time is available.
A possible but limited workaround would be to create what I would call a Data Transfer Object. Have a custom object that has all the values you would have and have it return the object of the type you want (or a translator that will convert the DTO that has all fields to the object you want with the map and some fields). The problem with this solution is that it forces you to know in advance what are all possible entries you have in the map.
I have a simple persistent pojo like:
public class Peristent {
private String unsafe;
}
I use Spring Data mongoTemplate to persist and fetch the above object. I also need to encrypt the Persistent.unsafe variable and store a complex representation of that in backend, everytime I try to save Persistent object.
Can I annotate Persistent, or provide some sort of hooks where I can make the aforementioned translations without me having to do that in the Pojo code manually. This has to happen automatically during mongoTemplate.insert.
Spring Data currently only support Type based conversions. There is an issue for supporting property based conversion, which you might want to track.
Therefore annotating won't work. What you could do is, create use a separate class for the property, which just wraps the String and register a custom converter for that type. See http://docs.spring.io/spring-data/data-mongo/docs/1.10.4.RELEASE/reference/html/#mongo.custom-converters for details, how to do that.
I am wondering if there is a way to wrap all argument resolvers like for #PathVariables or #ModelAttributes into one single transaction? We are already using the OEMIV filter but spring/hibernate is spawning too many transactions (one per select if they are not wrapped within a service class which is be the case in pathvariable resolvers for example).
While the system is still pretty fast I think this is not necessary and neither consistent with the rest of the architecture.
Let me explain:
Let's assume that I have a request mapping including two entities and the conversion is based on a StringToEntityConverter
The actual URL would be like this if we support GET: http://localhost/app/link/User_231/Item_324
#RequestMapping("/link/{user}/{item}", method="POST")
public String linkUserAndItem(#PathVariable("user") User user, #PathVariable("item") Item item) {
userService.addItem(user, item);
return "linked";
}
#Converter
// simplified
public Object convert(String classAndId) {
return entityManager.find(getClass(classAndId), getId(classAndId));
}
The UserService.addItem() method is transactional so there is no issue here.
BUT:
The entity converter is resolving the User and the Item against the database before the call to the Controller, thus creating two selects, each running in it's own transaction. Then we have #ModelAttribute methods which might also issue some selects again and each will spawn a transaction.
And this is what I would like to change. I would like to create ONE readonly Transaction
I was not able to find any way to intercept/listen/etc... by the means of Spring.
First I wanted to override the RequestMappingHandlerAdapter but the resolver calls are well "hidden" inside the invokeHandleMethod method...
The ModelFactory is not a spring bean, so i cannot write an interceptor either.
So currently I only see a way by completely replacing the RequestMappingHandlerAdapter, but I would really like to avoid that.
And ideas?
This seems like a design failure to me. OEMIV is usually a sign that you're doing it wrong™.
Instead, do:
#RequestMapping("/link/User_{userId}/Item_{itemId}", method="POST")
public String linkUserAndItem(#PathVariable("userId") Long userId,
#PathVariable("itemId") Long itemId) {
userService.addItem(userId, itemId);
return "linked";
}
Where your service layer takes care of fetching and manipulating the entities. This logic doesn't belong in the controller.
I'm new to Spring. I'm working on a MVC application that would works as follows:
1) user fills the form with data necessary to create the connection to some service
2) controller gets the data from input, create new object serviceManager and save this object e.g in some HashMap with serviceId
3) next time user wants to use this service, controller using serviceId reads data from HashMap.
So I simply need to store this HashMap throughout the whole session in my controller for future use. What would be the best way to accomplish that? Maybe creating serviceManager object each time and reading data from database is the proper solution? In my controller I'm already using #Autowired fields which perfectly serve the purpose, but they're defined in spring xml and I have to store the data dynamically.
Seems your requirement is kind of same with mine which I should keep the main data in the session and every time get the detail data from client and combine 2 kind of data to retrieve something from database. I just put the main part data in the session and then in the whole session that I can get it. I also try to use #SessionAttribute, but after tried dozens of time, I gave it up, it has a lots of problems. So if you can, I just recomment you to store the data in session, that's the samplest way.
I'm newish to spring myself, but as far as putting this in the session:
#Controller
#SessionAttributes({"myObject"})
public class MyController() {
...
#RequestMapping(value="/foo")
// Corrected as per Costi below
// public String someMethod(#PathVariable MyObject myObject) {
public String someMethod(#ModelAttribute MyObject myObject) {
...
}
}
#SessionAttributes will put a MyObject named myObject into the session, if it's not already there, and the #PathVariable pulls it down so you can use it in the method.
The curlys in session attributes aren't necessary for just one attribute, however, you can specify more than one, comma separated, when you use the array notation (which is to say: the curlys)
i realize that something wired goes on with a project i try to create. I'm using the RestTemplate. And i'm trying to connect with a server and retrieve data. All went good until the time i decide to broke my program in more than one controller classes. Look what i did. In each one of these new classes i insert at each one of them:
RestTemplate restTemplate= new RestTemplate();
In order to retrieve data i'm using the JAXB annotations only(for data binding) and i can retrieve whatever i want. But when i'm trying to execute this line of code in the new class:
ResponseEntity<AClass> result = restTemplate.exchange("url",
HttpMethod.GET, entity, AClass.class);
I'm taking this exception: RestClientException "Could not extract response: no suitable HttpMessageConverter found for response type [AClass] and content type [application/xml]"
If i put it back in the previous class can be executed without problem. Really i don't understand why. Probably because i'm using in the new class a new RestTemplate.I try to declare different RestTemplates and also to declare this way:#Autowired but the problem remains. Should i declare something new in the dispatcher servlet?Moreover can i call in many classes the object restTemplate(RestTemplate restTemplate=new RestTemplate()). Should i declare it in a specific class and call it from there? What should i do?I expect your propositions.
How did you previously have your restTemplate instance set up? You have to register a MarshallingHttpMessageConverter with the RestTemplate instance, or it won't know how to unmarshal the XML. Something like this:
Jaxb2Marshaller jaxbMarshaller = new Jaxb2Marshaller();
// Make sure context paths includes AClass's package
jaxbMarshaller.setContextPaths("com.example.generated");
MarshallingHttpMessageConverter converter = new
MarshallingHttpMessageConverter(jaxbMarshaller, jaxbMarshaller);
restTemplate.setMessageConverters(Arrays.<HttpMessageConverter<?>> asList(converter));