0

First of all I have SpringBootApplication. I declared a stored procedure there:

 public Get(JdbcTemplate jdbcTemplate) {
            setJdbcTemplate(jdbcTemplate);
            setSql(PROCEDURE);
    
      
  
    declareParameter(new SqlOutParameter("smth", Types.NUMERIC));
    declareParameter(new SqlOutParameter("clobData", Types.CLOB)); //json data
compile()

Here is the code for getting parametres, which is derived from controller:

public Base get(some parametres) throws SQLException {

        var res = super.execute(some parametres);

        final var err= (String) res.get("error");
        final var clobData = (Clob) res.get("clobName"); //json
//other code

return new Base(err, clobData);

Base.class

public class Base {
String err;
List<SecondClass> json;
}

Overall the response would be like this:

{
"err": "smth",
"acc": "123",
"clobData": [{
"one": "two"
...
    }, { ...
}]
}

I am able to retrieve all fields except field with Clob (json) data. I dont know whats the problem. Maybe I should cast it to String (I couldn't, because the response body said that Clob can't be cast to String) and show somehow? Or there is a solution with Json object?

AlexWhite
  • 123
  • 2
  • 15

2 Answers2

1

You need to read clob:

StringBuilder sb = new StringBuilder();
try (Reader reader = clobData.getCharacterStream(); 
    BufferedReader br = new BufferedReader(reader)) {
    String line;
    while(null != (line = br.readLine())) {
       sb.append(line);
    }
} catch (Exception e) {
    //handle error
}
String json = sb.toString();
Ori Marko
  • 56,308
  • 23
  • 131
  • 233
1

I think there are two different issues.

On one hand, you need to convert the CLOB retrieved from the database to String. For that purpose, the solution proposed by @user7294900 is very good. In any way, please, consider read this related SO question, it provides other alternatives as well.

In any or other way, consider that you have a method that, given a CLOB, returns a String:

public String getClobAstring(Clob clob) {
  //... Any tf the implementations mentioned
} 

Now, the second problem has to do with how to return that information as JSON.

It will be very dependent on how you are serializing your object model to JSON and how your CLOB information looks like.

For instance, supposing that you are using Jackson, and that your CLOB information is an object without arrays and nested objects, you can try the following.

ObjectMapper mapper = new ObjectMapper();
Clob clob = (Clob) result.get("clob");
String json = getClobAstring(clob);
// If the structure of the json stored as Clob is simple,
// you can convert it to a Map, for example
Map<String, String> clobData = mapper.readValue(json, Map.class);
// If you face any error with the last line above, try using a `TypeReference` instead
// Map<String, String> clobData = mapper.readValue(json, new TypeReference<HashMap<String,String>>(){});


// Instantiate `GetResp` and associate clobData
GetResp getResp = new GetResp(); 
getResp.setClobData(clobData)

Of course, this will only be valid if, as mentioned, the structure of the JSON stored as CLOB is simple; if that it is not the case, you can create an object that hold the clob model:

public class ClobData {
  private String one;

  //...
}

and convert it appropriately:

ObjectMapper mapper = new ObjectMapper();
Clob clob = (Clob) result.get("clob");
String json = getClobAstring(clob);
// If the structure of the json stored as Clob is simple,
// you can convert it to a Map, for example
ClobData clobData = mapper.readValue(json, ClobData.class);

// Instantiate `GetResp` and associate clobData
GetResp getResp = new GetResp(); 
getResp.setClobData(clobData)

Equivalently, according to your last comments:

// Get CLOB from the database
Clob clob = (Clob) result.get("clob");

// Convert to string using one of the mentioned methods
String json = getClobAstring(clob);

// Now, you need to convert this json String to something else.
// The process will be very dependent of the underlying JSON processing library
// I assume you are using Jackson.
// In order to read the returned json, first build an ObjectMapper
ObjectMapper mapper = new ObjectMapper();

// For your comments, you are using the class SecondClass
SecondClass secondClass = mapper.readValue(json, SecondClass.class);

//If instead, you are returning a list of objects, for example, try this:
// SecondClass[] secondClasses = mapper.readValue(json, SecondClass[].class);

// Or this, it is the same
// List<SecondClass> secondClasses = mapper.readValue(json, new TypeReference<List<SecondClass>>(){});

// Then, you need to provide that information to the Base class

// In some place you instantiated it, it is not really important how
Base base = new Base(); 

// Then, I suppose you should provide the SecondClass information to
// that instance
base.setFieldWithJson(secondClass);

// Or:
//base.fieldWithJson = secondClass;

Once constructed, as you are using Spring Boot, you can return this json to the client using the standard mechanisms provided by the framework. For instance, in your Controller define the following method - please, note the @ResponseBody annotation, it will instruct Spring to serialize the returned object to JSON and return to the client in the HTTP response:

@GetMapping("/base-information")
// As mentioned, annotate your method with @ResponseBody
@ResponseBody
// You can provide the parameters that you need, in the way you need
public Base getBaseInformation(@RequestBody params) {
  // Based on the parameters, interact with your services and
  // its the underlying the database, what deemed appropriate
  // to fill base fields
  String err = result.get("err");
  String acc = result.get("acc");
  // in the case of the blob field, transform it as mentioned
  Clob clob = (Clob) result.get("clob");
  String json = getClobAstring(clob);
  ObjectMapper mapper = new ObjectMapper();
  List<SecondClass> fieldWithJson = mapper.readValue(json, new TypeReference<List<SecondClass>>(){});

  // Now, create an instance of the Base class and fill its 
  // different fields with the values obtained
  Base base = new Base();
  base.setErr(err);
  base.setAcc(acc);
  base.setFieldWithJson(fieldWithJson);

  // If you prefer, you can pass all this information in the class
  // constructor, for example
  // Base base = new Base(err, acc, fieldWithJson);
  
  // Then, return base with all the information fulfilled
  // Spring will handle json conversion for you as the method is 
  // annotated with @ResponseBody.
  return base;
}

If you are using another library, GSON, Moshi, etcetera, you will find similar approaches as well.

jccampanero
  • 50,989
  • 3
  • 20
  • 49
  • my Clob field has list of json – AlexWhite Jul 14 '21 at 10:51
  • Do I need to setClobData()? If I am retrieving this data from DB? – AlexWhite Jul 14 '21 at 10:54
  • hi @AlexWhite. Sorry, please, what do you mean by a list of json? An array of objects? An object with several fields? Well, in fact it depends on how you return the information to the client. I told you to set the field with a setter, with `setClobData`, but you can pass the club information as a constructor argument or by any other mean: the only important thing is to convert the Clob to whatever you consider appropriate, a `Map`, a custom object. That it makes sense to you? Please, can you further explain how will you return the information to the client? – jccampanero Jul 14 '21 at 11:30
  • Do you use an object that agglutinates the overall response, for instance? – jccampanero Jul 14 '21 at 11:31
  • I mean, when I retrieve fields from db, I could retrieve the fields with only one data , but having trouble retrieving field with list of json. Here how my model looks: class Base { String field1; String field2; SecondClass fieldWithJson //here I will have json} – AlexWhite Jul 14 '21 at 12:22
  • Hi @AlexWhite. Please, see the updated answer. Is it more clear? – jccampanero Jul 14 '21 at 12:34
  • Its pretty clear. But one thing left. How should I have my model. Am I doing right that I have SecondClass with fields that my json have? And How then I can return list of this fields? class SecondClass { String name; Number age;// thats sayin the keys that my json will have}. My class Base is above. Maybe I should have List fieldWithJson in my Base class? – AlexWhite Jul 14 '21 at 12:46
  • You are right @AlexWhite. If the json returned contains a list of values, an array of values, you can define `fieldWithJson` in `Base` as `List fieldWithJson`. Then, use the provided suggestion: `List secondClasses = mapper.readValue(json, new TypeReference>(){});` in the answer to parse as json that list, and set the value in the `Base` class: `base.setFieldWithJson(secondClasses);`. – jccampanero Jul 14 '21 at 12:57
  • The only thing is what should I return? I am returning my fields like that: return new (field1, field2, ...//how should I return my json then after setting?) – AlexWhite Jul 14 '21 at 13:13
  • It will again depend on the actual mechanism you are using. If you use Spring and Spring MVC, returning the object from your controller itself, if annotated with @responsebidy` will very likely return json to the client. In addition, you can serialize your base class to `String` and write to the http response, for instance: `String jsonToBeReturned = mapper.writeValueAsString(base);`. I hope you get the idea – jccampanero Jul 14 '21 at 13:20
  • I am using SpringBoot with Jdbc Template. I need to return Base class type, because I have fields in my Base class (my model). – AlexWhite Jul 14 '21 at 13:35
  • @AlexWhite Then, just return your the `Base` instance you created from the controller method, and either annotate your Controller as `@RestController` or your method as `@ResponseBody`: Spring will handle automatically the json conversion for you – jccampanero Jul 14 '21 at 14:31
  • Please, see the updated answer. I hope it helps. – jccampanero Jul 14 '21 at 14:38
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/234881/discussion-between-alexwhite-and-jccampanero). – AlexWhite Jul 15 '21 at 04:46
  • I already have @RequestBody as parameter, which will have 2 fields as input parameters from user, but thats not a problem now. A problem is how should I return data. Please, have a look at my updated code (Base get method). And how I am returning fields like return new Base (err, clobData). I can't just return only base. How about other fields in the Base class? – AlexWhite Jul 15 '21 at 04:51
  • Hi @AlexWhite. Morning. Yes, of course: when I said that you should return the base class, I mean that you will **return the base class with all the fields populated with the values obtained from the database**. Please, see the updated answer. Does it make sense to you? Also, note that I mentioned ResponseBody, not RequestBody: they are two different things and serve for different purposes, although both of them are related with JSON serialization. Please, see: https://www.baeldung.com/spring-request-response-body – jccampanero Jul 15 '21 at 08:54
  • Everything is good, except my json (clob) field doesnt returning nothing ) What may be a problem? Is it the problem of clobToString casting or perhaps I dont have nothing in my clob? Is there a way to check what is returning my clob? – AlexWhite Jul 15 '21 at 13:53
  • Hi @AlexWhite. Thank you very much. I am happy to hear that everything is fine, although on the other hand I am sorry because it seems that the clob is not being returned properly. Yes: if you have any tool, say SQL Developer, TOAD for Oracle, etcetera, look in the database if the value is there; just select the appropriate row and try to save to the filesystem or simple run any query against it. From Java, try debugging your controller or emit logging statements, `System.out.println` if you prefer. In here statements, try printing the Clob size, for example (`clob.length()`) or the `String` – jccampanero Jul 15 '21 at 15:29
  • obtained as the result to the performed conversion. Please, can you try? – jccampanero Jul 15 '21 at 15:29
  • Let's say that my clobToString returns "{\"filed1\": \"value1\" , \"field2\": 123}"; //other fields just null. Now the problem stuck here: ObjectMapper mapper = new ObjectMapper(); List fieldWithJson = mapper.readValue(json, new TypeReference>(){}); How will look like the result of this? I mean when I am trying mapping it. How it will recognize my fields? – AlexWhite Jul 16 '21 at 07:16
  • 1
    Hi @AlexWhite. It will try to match every field in the json `String` with the corresponding field in your `SecondClass` class. For instance, in the example you provided, your SecondClass class will look like this `public class SecondClass{ private String field1; private int field2; // getters and setters }`. Pay attention to one thing: if your json string is an array, if it starts with `[` and `]`, then the provided code with `List` is right; if your json just look like `"{\"filed1\": \"value1\" , \"field2\": 123}"`, without an array, see next comment – jccampanero Jul 16 '21 at 10:18
  • you need to do this: `SecondClass fieldWithJson = mapper.readValue(json, SecondClass.class);`. You should always provide the right target object as the result of the mapping process. – jccampanero Jul 16 '21 at 10:19
  • Get it! Thats question just for interest. Clob should be transferred to String or it can be transferred to another data type? – AlexWhite Jul 16 '21 at 11:03
  • That is nice Alex!! A `CLOB` should be transferred to `String` because by definition is a text based large object. Having said that, as you are using Oracle, please, be aware that in recent database versions if provides support for [native JSON processing](https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/sql/json/package-summary.html) from Java and JDBC. Please, pay attention in that this is a feature only of the Oracle JDBC driver, non portable to other platforms in contrast to the solution you implemented. – jccampanero Jul 16 '21 at 11:14
  • How will it look like in my term? I can just create JsonObject and pass my clob without transferring it to String? – AlexWhite Jul 16 '21 at 11:23
  • In case of using the Oracle native support for json, you will not work with `CLOB`s but with Oracle specific JSON datatypes. You will need to adapt your insertion logic to create these Oracle types. In addition you will need to perform conversion between the information received from the client and the one you will return to the client but instead of transforming a `CLOB` to `String` and later process it with Jackson, you will need to process the returned, say `OracleJsonObject`, and convert this object to you `SecondClass`, I hope you get the idea. In my opinion, the actual solution is best – jccampanero Jul 16 '21 at 11:33