How to store Java 8 (JSR-310) dates in elasticsearch - elasticsearch

I know elasticsearch can only save Date types internally. But can i make it aware to store/convert Java 8 ZonedDateTime, as i use this type in my entity?
I'm using spring-boot:1.3.1 + spring-data-elasticsearch with jackson-datatype-jsr310 on the classpath. No conversions seem to apply neither when i try to save a ZonedDateTime nor Instant or something else.

One way of doing this is to create custom converter like this:
import com.google.gson.*;
import java.lang.reflect.Type;
import java.time.ZonedDateTime;
import static java.time.format.DateTimeFormatter.*;
public class ZonedDateTimeConverter implements JsonSerializer<ZonedDateTime>, JsonDeserializer<ZonedDateTime> {
#Override
public ZonedDateTime deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
return ZonedDateTime.parse(jsonElement.getAsString(), ISO_DATE_TIME);
}
#Override
public JsonElement serialize(ZonedDateTime zonedDateTime, Type type, JsonSerializationContext jsonSerializationContext) {
return new JsonPrimitive(zonedDateTime.format(ISO_DATE_TIME));
}
}
and then configure JestClientFactory to use this converter:
Gson gson = new GsonBuilder()
.registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeConverter()).create();
JestClientFactory factory = new JestClientFactory();
factory.setHttpClientConfig(new HttpClientConfig
.Builder("elastic search URL")
.multiThreaded(true)
.gson(gson)
.build());
client = factory.getObject();
Hope it'll help.

Related

Spring-data-elasticsearch: cannot convert from Flux<SearchHit<Sugestao>> to Flux<Sugestao> after updated to 7.6.2. How deal with SearchHit?

Context: I want to use ElasticSearch in a full reactive stack compound by ElasticSearch and Spring WebFlux.
It is my first time using springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient and springframework.data.elasticsearch.core.ReactiveElasticsearchOperations. I have worked in a reactive stack using MongoDb but it is my first time with ElasticSearch.
I have successfully follow a tutorial using ReactiveElasticsearchOperations with spring-data-elasticsearch-3.2.6 and elasticsearch-6.8.7 (Elastic Tutorial)
And the findAll/findById are working properly with elastic-6.8.7 and spring-data-elasticsearch-3.2.6
MyModelService:
...
private final ReactiveElasticsearchOperations reactiveElasticsearchOperations;
private final ReactiveElasticsearchClient reactiveElasticsearchClient;
public MyModelServiceImpl(ReactiveElasticsearchOperations reactiveElasticsearchOperations,
ReactiveElasticsearchClient reactiveElasticsearchClient) {
this.reactiveElasticsearchOperations = reactiveElasticsearchOperations;
this.reactiveElasticsearchClient = reactiveElasticsearchClient;
}
#Override
public Mono<MyModel> findMyModelById(String id){
return reactiveElasticsearchOperations.findById(
id,
MyModel.class,
MYMODEL_ES_INDEX,
DEFAULT_ES_DOC_TYPE
).doOnError(throwable -> logger.error(throwable.getMessage(), throwable));
}
#Override
public Flux<MyModel> findAllMyModels(String field, String value){
NativeSearchQueryBuilder query = new NativeSearchQueryBuilder();
if (!StringUtils.isEmpty(field) && !StringUtils.isEmpty(value)) {
query.withQuery(QueryBuilders.matchQuery(field, value));
}
return reactiveElasticsearchOperations.find(
query.build(),
MyModel.class,
MYMODEL_ES_INDEX
).doOnError(throwable -> logger.error(throwable.getMessage(), throwable));
}
I try follow same idea with updated versions (spring-data-elasticsearch-4 and elast-7.6.2. Since I can read "Deprecated. since 4.0, use search(Query, ...) Flux emitting matching entities one by one wrapped in a SearchHit." then I got completely stuck because the result is wrraped in SearchHit. Well, searching around I din't get the idea why such wrrapper neither how to convert/map/flatMap/etc to a Flux of my model to return by controller method.
Here is my tentative causing the issue mentioned on this question topic:
service:
import com.poc.favoritos.model.Sugestao;
import org.elasticsearch.index.query.QueryBuilders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public class SugestaoServiceImpl implements SugestaoService{
private static final Logger logger = LoggerFactory.getLogger(SugestaoServiceImpl.class);
private final ReactiveElasticsearchOperations reactiveElasticsearchOperations;
private final ReactiveElasticsearchClient reactiveElasticsearchClient;
public SugestaoServiceImpl(ReactiveElasticsearchOperations reactiveElasticsearchOperations,
ReactiveElasticsearchClient reactiveElasticsearchClient) {
this.reactiveElasticsearchOperations = reactiveElasticsearchOperations;
this.reactiveElasticsearchClient = reactiveElasticsearchClient;
}
#Override
public Mono<Sugestao> findSugestaoById(String id) {
return reactiveElasticsearchOperations.get(id, Sugestao.class)
.doOnError(throwable -> logger.error(throwable.getMessage(), throwable));
}
#Override
public Flux<Sugestao> findAllMySugestoes(String field, String value) {
NativeSearchQueryBuilder query = new NativeSearchQueryBuilder();
if (!StringUtils.isEmpty(field) && !StringUtils.isEmpty(value)) {
query.withQuery(QueryBuilders.matchQuery(field, value));
}
return reactiveElasticsearchOperations.search(query.build(), Sugestao.class);
}
}
ElastiSearchConfig orinally copied from Same tutorial mentioned above . Honestly, I am not sure why do I need and what is this config adding to my project. BTW, I am studding it also from operations reference.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.client.reactive.ReactiveRestClients;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
#Configuration
public class ElasticsearchConfig {
#Bean
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo(elassandraHostAndPort)
.withWebClientConfigurer(webClient -> {
ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
.codecs(configurer -> configurer.defaultCodecs()
.maxInMemorySize(-1))
.build();
return webClient.mutate().exchangeStrategies(exchangeStrategies).build();
})
.build();
return ReactiveRestClients.create(clientConfiguration);
}
#Bean
public ElasticsearchConverter elasticsearchConverter() {
return new MappingElasticsearchConverter(elasticsearchMappingContext());
}
#Bean
public SimpleElasticsearchMappingContext elasticsearchMappingContext() {
return new SimpleElasticsearchMappingContext();
}
#Bean
public ReactiveElasticsearchOperations reactiveElasticsearchOperations() {
return new ReactiveElasticsearchTemplate(reactiveElasticsearchClient(), elasticsearchConverter());
}
#Value("${spring.data.elasticsearch.client.reactive.endpoints}")
private String elassandraHostAndPort;
}
As for the SearchHit: This class contains information form a search result that is not part of the entity, but part of the search result like score, sort values, highlight entries.
If you don't need this and just want to have a Flux with the entity alone:
Flux<SearchHit<Entity>> fluxSearchHits = ...
Flux<Entity> fluxEntity = fluxSearchHits.map(searchHit -> searchHit.getContent);
As for the configuration:
you need the ReactiveElasticsearchClient bean to configure Spring Data Elasticsearch. The other 3 beans: Don't know why they are there; they are not needed for Spring Data Elasticsearch 4.0
Edit 16.05.2020:
The configuration: You should derive your configuration class from AbstractReactiveElasticsearchConfiguration, then you don't need the other beans, because the base class defines the necessary things:
#Configuration
public class ElasticsearchConfig extends AbstractReactiveElasticsearchConfiguration{
#Value("${spring.data.elasticsearch.client.reactive.endpoints}")
private String elassandraHostAndPort;
#Bean
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo(elassandraHostAndPort)
.build();
return ReactiveRestClients.create(clientConfiguration);
}
}
and the customized WebClientConfiguration is only needed if you retrieve large result sets and the default memory size for the result buffer is too low.

Get Aggregate Information from Elasticsearch using Spring-data-elasticsearch, ElasticsearchRepository

I would like to get aggregate results from ES like avgSize (avg of a field with name 'size'), totalhits for documents that match a term, and some other aggregates in future, for which I don't think ElasticsearchRepository has any methods to call. I built Query and Aggregate Builders as below. I want to use my Repository interface but I am not sure of what should the return ObjectType be ? Should it be a document type in my DTOs ? Also I have seen examples where the searchQueryis passed directly to ElasticsearchTemplate but then what is the point of having Repository interface that extends ElasticsearchRepository
Repository Interface
public interface CCFilesSummaryRepository extends ElasticsearchRepository<DataReferenceSummary, UUID> {
}
Elastic configuration
#Configuration
#EnableElasticsearchRepositories(basePackages = "com.xxx.repository.es")
public class ElasticConfiguration {
#Bean
public ElasticsearchOperations elasticsearchTemplate() throws UnknownHostException {
return new ElasticsearchTemplate(elasticsearchClient());
}
#Bean
public Client elasticsearchClient() throws UnknownHostException {
Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
TransportClient client = new PreBuiltTransportClient(settings);
client.addTransportAddress(new TransportAddress(InetAddress.getLocalHost(), 9200));
return client;
}
}
Service Method
public DataReferenceSummary createSummary(final DataSet dataSet) {
try {
QueryBuilder queryBuilder = QueryBuilders.matchQuery("type" , dataSet.getDataSetCreateRequest().getContentType());
AvgAggregationBuilder avgAggregationBuilder = AggregationBuilders.avg("avg_size").field("size");
ValueCountAggregationBuilder valueCountAggregationBuilder = AggregationBuilders.count("total_references")
.field("asset_id");
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(queryBuilder)
.addAggregation(avgAggregationBuilder)
.addAggregation(valueCountAggregationBuilder)
.build();
return ccFilesSummaryRepository.search(searchQuery).iterator().next();
} catch (Exception e){
e.printStackTrace();
}
return null;
}
DataReferernceSummary is just a POJO for now and for which I am getting an error during my build that says Unable to build Bean CCFilesSummaryRepository, illegalArgumentException DataReferernceSummary. is not a amanged Object
First DataReferenceSummary must be a class annotated with #Document.
In Spring Data Elasticsearch 3.2.0 (the current version) you need to define the repository return type as AggregatedPage<DataReferenceSummary>, the returned object will contain the aggregations.
From the upcoming version 4.0 on, you will have to define the return type as SearchHits<DataReferenceSummary> and find the aggregations in this returned object.

JAXBElement: providing codec (/converter?) for class java.lang.Class

I have been evaluating to adopt spring-data-mongodb for a project. In summary, my aim is:
Using existing XML schema files to generate Java classes.
This is achieved using JAXB xjc
The root class is TSDProductDataType and is further modeled as below:
The thing to note here is that ExtensionType contains protected List<Object> any; allowing it to store Objects of any class. In my case, it is amongst the classes named TSDModule_Name_HereModuleType and can be browsed here
Use spring-data-mongodb as persistence store
This is achieved using a simple ProductDataRepository
#RepositoryRestResource(collectionResourceRel = "product", path = "product")
public interface ProductDataRepository extends MongoRepository<TSDProductDataType, String> {
TSDProductDataType queryByGtin(#Param("gtin") String gtin);
}
The unmarshalled TSDProductDataType, however, contains JAXBElement which spring-data-mongodb doesn't seem to handle by itself and throws a CodecConfigurationException org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class java.lang.Class.
Here is the faulty statement:
TSDProductDataType tsdProductDataType = jaxbElement.getValue();
repository.save(tsdProductDataType);
I tried playing around with Converters for spring-data-mongodb as explained here, however, it seems I am missing something since the exception is about "Codecs" and not "Converters".
Any help is appreciated.
EDIT:
Adding converters for JAXBElement
Note: Works with version 1.5.6.RELEASE of org.springframework.boot::spring-boot-starter-parent. With version 2.0.0.M3, hell breaks loose
It seems that I missed something while trying to add converter earlier. So, I added it like below for testing:
#Component
#ReadingConverter
public class JAXBElementReadConverter implements Converter<DBObject, JAXBElement> {
//#Autowired
//MongoConverter converter;
#Override
public JAXBElement convert(DBObject dbObject) {
Class declaredType, scope;
QName name = qNameFromString((String)dbObject.get("name"));
Object rawValue = dbObject.get("value");
try {
declaredType = Class.forName((String)dbObject.get("declaredType"));
} catch (ClassNotFoundException e) {
if (rawValue.getClass().isArray()) declaredType = List.class;
else declaredType = LinkedHashMap.class;
}
try {
scope = Class.forName((String) dbObject.get("scope"));
} catch (ClassNotFoundException e) {
scope = JAXBElement.GlobalScope.class;
}
//Object value = rawValue instanceof DBObject ? converter.read(declaredType, (DBObject) rawValue) : rawValue;
Object value = "TODO";
return new JAXBElement(name, declaredType, scope, value);
}
QName qNameFromString(String s) {
String[] parts = s.split("[{}]");
if (parts.length > 2) return new QName(parts[1], parts[2], parts[0]);
if (parts.length == 1) return new QName(parts[0]);
return new QName("undef");
}
}
#Component
#WritingConverter
public class JAXBElementWriteConverter implements Converter<JAXBElement, DBObject> {
//#Autowired
//MongoConverter converter;
#Override
public DBObject convert(JAXBElement jaxbElement) {
DBObject dbObject = new BasicDBObject();
dbObject.put("name", qNameToString(jaxbElement.getName()));
dbObject.put("declaredType", jaxbElement.getDeclaredType().getName());
dbObject.put("scope", jaxbElement.getScope().getCanonicalName());
//dbObject.put("value", converter.convertToMongoType(jaxbElement.getValue()));
dbObject.put("value", "TODO");
dbObject.put("_class", JAXBElement.class.getName());
return dbObject;
}
public String qNameToString(QName name) {
if (name.getNamespaceURI() == XMLConstants.NULL_NS_URI) return name.getLocalPart();
return name.getPrefix() + '{' + name.getNamespaceURI() + '}' + name.getLocalPart();
}
}
#SpringBootApplication
public class TsdApplication {
public static void main(String[] args) {
SpringApplication.run(TsdApplication.class, args);
}
#Bean
public CustomConversions customConversions() {
return new CustomConversions(Arrays.asList(
new JAXBElementReadConverter(),
new JAXBElementWriteConverter()
));
}
}
So far so good. However, how do I instantiate MongoConverter converter;?
MongoConverter is an interface so I guess I need an instantiable class adhering to this interface. Any suggestions?
I understand the desire for convenience in being able to just map an existing domain object to the database layer with no boilerplate, but even if you weren't having the JAXB class structure issue, I would still be recommending away from using it verbatim. Unless this is a simple one-off project, you almost definitely will hit a point where your domain models will need to change but your persisted data need to remain in an existing state. If you are just straight persisting the data, you have no mechanism to convert between a newer domain schema and an older persisted data scheme. Versioning of the persisted data scheme would be wise too.
The link you posted for writing the customer converters is one way to achieve this and fits in nicely with the Spring ecosystem. That method should also solve the issue you are experiencing (about the underlying messy JAXB data structure not converting cleanly).
Are you unable to get that method working? Ensure you are loading them into the Spring context with #Component plus auto-class scanning or manually via some Configuration class.
EDIT to address your EDIT:
Add the following to each of your converters:
private final MongoConverter converter;
public JAXBElement____Converter(MongoConverter converter) {
this.converter = converter;
}
Try changing your bean definition to:
#Bean
public CustomConversions customConversions(#Lazy MongoConverter converter) {
return new CustomConversions(Arrays.asList(
new JAXBElementReadConverter(converter),
new JAXBElementWriteConverter(converter)
));
}

How to consume a spring data rest service with java?

I have the following spring boot + data Rest repository:
#RepositoryRestResource(collectionResourceRel = "dto", path = "produtos")
public interface ProdutoRepository extends CrudRepository<Produto, Integer> {
#Query("SELECT p FROM Produto p where descricao LIKE CONCAT(UPPER(:like),'%')")
List<Produto> findByLike(#Param("like") String like);
}
I also have a java client that access this method (this is my example of doing it):
String url = "http://localhost:8080/produtos/search/findByLike?like={like}";
RestTemplate t = new RestTemplate();
ProdutoDto resp = t.getForObject(url, ProdutoDto.class, txtLoc.getText());
ProdutoDto (this one is not totally necessary):
public class ProdutoDto extends HalDto<Produto> {}
HalDto:
public class HalDto<T extends ResourceSupport> extends ResourceSupport {
#JsonProperty("_embedded")
private EmbeddedDto<T> embedded;
public EmbeddedDto<T> getEmbedded() {
return embedded;
}
public void setEmbedded(EmbeddedDto<T> embedded) {
this.embedded = embedded;
}
}
EmbeddedDto:
public class EmbeddedDto<T> {
#JsonProperty("dto")
private List<T> dtoList;
public List<T> getDtoList()
{
return dtoList;
}
public void setDto(List<T> dtoList) {
this.dtoList = dtoList;
}
}
Those classes are necessary (i think) because Spring Data returns data in the HAL (https://en.wikipedia.org/wiki/Hypertext_Application_Language) format.
Note: Produto must extend ResourceSupport.
Caveats: All collectionResourceRel must be named "dto" and it only works for collections (may be adjusted).
Is this the proper way to do this?
I have googled around and found plenty of examples of doing the server side, but almost nothing on building clients.
Thanks.
This is a solution that I have found which seems to work well.
First, setup your RestTemplate so that it expects JSON/HAL and knows what to do with it:
#Bean
public RestTemplate restTemplate() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jackson2HalModule());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MappingJackson2HttpMessageConverter messageConverter =
new MappingJackson2HttpMessageConverter();
messageConverter.setObjectMapper(objectMapper);
messageConverter.setSupportedMediaTypes(Arrays.asList(MediaTypes.HAL_JSON, MediaType.APPLICATION_JSON_UTF8));
return new RestTemplate(Arrays.asList(messageConverter));
}
Then you can use the exchange method of the RestTemplate to specify that you want your result to be ResponseEntity<PagedResources<Producto>>
ResponseEntity<PagedResources<Producto>> resultResponse = restTemplate.exchange(uri, HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference<PagedResources<Producto>>(){});
if(resultResponse.getStatusCode() == HttpStatus.OK){
Collection<Producto> results = resultResponse.getBody().getContent();
log.info("{} results obtained", results.size());
}
You can instantiate restTemplate by either calling the restTemplate() method defined above or you can inject (autowire) it.

Calling through Spring a procedure with collection of object (oracle ARRAY STRUCT)

im trying to execute a procedure which contains between others a parameter which is a collection of object (oracle). I have managed them lot of times without spring, but I'm a bit lost trying to do it with spring, althoug there is some information on the internet, I can't find a full example in order to compare my code. Spring doc has just fragments. Probably my code is wrong but i ignore why, could you help me? I'm running simplier procedures without problems. My DAO looks like this:
//[EDITED]
private SimpleJdbcCall pActualizaDia;
....
#Autowired
public void setDataSource(DataSource dataSource) {
pActualizaDia = new SimpleJdbcCall(dataSource).withCatalogName("PTR_GRUPOS_TRABAJO").withProcedureName("UPDATE_DIA");
pActualizaDia.getJdbcTemplate().setNativeJdbcExtractor(new OracleJdbc4NativeJdbcExtractor());
}
...
public Calendario updateSingle(final Calendario calendario) {
SqlTypeValue cambiosEmpresa = new AbstractSqlTypeValue() {
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
ArrayDescriptor arrayDescriptor = new ArrayDescriptor("TTPTR_CAMBIO_EMPRESA", conn);
Object[] collection = new Object[calendario.getCambiosEmpresa().size()];
int i = 0;
for (CeAnoEmp ce : calendario.getCambiosEmpresa()) {
collection[i++] = new STRUCT(new StructDescriptor("TPTR_CAMBIO_EMPRESA", conn), conn, new Object[] {
ce.getSQLParam1(),
//...more parameters here in order to fit your type.
ce.getSQLparamn() });
}
ARRAY idArray = new ARRAY(arrayDescriptor, conn, collection);
return idArray;
}
};
MapSqlParameterSource mapIn = new MapSqlParameterSource();
mapIn.addValue("P_ID_ESCALA", calendario.getEscala().getIdEscala());
//more simple params here
//Here it is the Oracle ARRAY working properly
pActualizaDia.declareParameters(new SqlParameter("P_CAMBIOS_EMPRESA",
OracleTypes.STRUCT, "TTPR_CAMBIO_EMPRESA"));
mapIn.addValue("P_CAMBIOS_EMPRESA",cambiosEmpresa);
//When executing the procedure it just work :)
pActualizaDia.execute(mapIn);
return null;
}
The exception I get sais
java.lang.ClassCastException: $Proxy91 cannot be cast to oracle.jdbc.OracleConnection
I've been reading more about this topic and i found that It almost seems like if using Oracle Arrays you also have to cast the connection to be an oracle connection.
However, most Spring jdbc framework classes like SimpleJDBCTemplate and StoredProcedure hide the connection access from you. Do I need to subclass one of those and override a method somewhere to get the dbcp connection and then cast it to an Oracle connection?
Thank you very much.
I've solved it finally, I've edited the post in order to have an example for anyone looking for a piece of code to solve this issue.
There are two important things to have in mind:
1) It's mandatory to set oracle extractor in jdbctemplate in order to cast properly the connection to get oracle functionality.
2) When using this extractor ojdbc and JRE version must be the same, any other case you'll get an abstractmethodinvocation exception.
Thanks anyone who tried to solve it and hope it helps.
you can use spring to call a procedure with array of collection of oracle structure : below a simple example to do this
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.sql.DataSource;
import oracle.jdbc.driver.OracleConnection;
import oracle.jdbc.driver.OracleTypes;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.StoredProcedure;
public class SpringObjectMapper {
public static class SaveObjectFunction extends StoredProcedure {
final static Logger logger = LoggerFactory.getLogger(SavePackInterFunction.class);
private static final String PROC_NAME = "schema.proc_name";
private final static String ARRAY_OF_VALUE_PARAM_NAME = "ARRAY_OF_VALUE";
private final static String OUT_PARAM_NAME = "out";
public SaveObjectFunction(DataSource dataSource) {
super(dataSource, PROC_NAME);
declareParameter(new SqlParameter(ARRAY_OF_VALUE_PARAM_NAME, OracleTypes.ARRAY, "schema.array_object_type"));
compile();
}
public String execute(Collection<Model> values) {
logger.info("------------------------EnregInterlocuteurPrcedure::execute : begin----------------------------");
String message = null;
try {
OracleConnection connection = getJdbcTemplate().getDataSource().getConnection().unwrap(OracleConnection.class);
ArrayDescriptor arrayValueDescriptor = new ArrayDescriptor("schema.array_object_type", connection);
StructDescriptor typeObjeDescriptor = new StructDescriptor("schema.object_type", connection);
Object[] valueStructArray = new Object[values.size()];
int i = 0;
for (Iterator<Model> iterator = values.iterator(); iterator.hasNext();) {
Model model = (Model) iterator.next();
STRUCT s = new STRUCT(typeObjeDescriptor, connection, new Object[] {model.getAttribute1(), model.getAttribute2(), model.getAttribute3(),
model.getAttribute4(), model.getAttribute5(), model.getAttribute6(), model.getAttribute7()});
valueStructArray[i++] = s;
}
ARRAY inZoneStructArray = new ARRAY(arrayValueDescriptor, connection, valueStructArray);
Map<String, Object> inputs = new HashMap<String, Object>();
inputs.put(ARRAY_OF_VALUE_PARAM_NAME, inZoneStructArray);
Map<String, Object> out = super.execute(inputs);
message = (String) out.get(OUT_PARAM_NAME);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return message;
}
}
}

Resources