_class property in CouchBase - couchbase

I have a document stored in Couchbase.
{
"a": {
"b": {
"key":"Value"
},
"_class":"com.nikhil.model"
},
"c":{
"d":{
"key":"value"
},
// _class is missing here
},
"_class": "com.nikhil.model"
}
Here as you can see I don't have an _class inside the "d" in the doucument because of this I am not able to get this document. An object mapping exception came.
_class is used to map the nested object of couchbase to the model required for mapping but inside the "c" object I don't have this _Class property that is why a mapping exception comes.
Is there any fix for this?

If you are using Spring boot, you need to override the typekey() method in the Couchbase Config file which extends AbstractCouchbaseConfiguration and return MappingCouchbaseConverter.TYPEKEY_SYNCGATEWAY_COMPATIBLE. This will replace your _class with javaClass string in the documents stored in Couchbase Server. I hope this helps.
#Configuration
public class RemoteCouchbaseConfiguration extends AbstractCouchbaseConfiguration {
#Value("${couchbase.host}")
private String host;
#Value("${couchbase.bucket.bucketName}")
private String bucketName;
#Value("${couchbase.bucket.password}")
private String password;
#Override
protected List<String> getBootstrapHosts() {
return Arrays.asList(this.host);
}
#Override
protected String getBucketName() {
return this.bucketName;
}
#Override
protected String getBucketPassword() {
return this.password;
}
#Override
public String typeKey() {
return MappingCouchbaseConverter.TYPEKEY_SYNCGATEWAY_COMPATIBLE;
}
}

Looks like you are using Couchbase with Spring Data, the easiest way is to return a projection:
#Override
public List<UserVO> getUsers(String companyId, List<String> userIds) {
String queryString = "SELECT meta(t).id as id, t.login as login, t.firstName as firstName from " + getBucketName() + " t where t."+getClassFilter()+" "
+ " and t.companyId = '" + companyId + "' and t.isEnabled = true and t.isVisible = true "
+ " and meta(t).id in ["+userIds.stream().map(e->"'"+e+"'").collect( Collectors.joining( "," )) +"]";
N1qlParams params = N1qlParams.build().consistency(ScanConsistency.NOT_BOUNDED).adhoc(true);
ParameterizedN1qlQuery query = N1qlQuery.parameterized(queryString, JsonObject.create(), params);
return userRepository.getCouchbaseOperations().findByN1QLProjection(query, UserVO.class);
}

You could add _class to it using an UPDATE N1QL statement like this:
UPDATE mybucket b
SET b.c.d._class = 'com.foo.bar'
WHERE b.c.d IS NOT MISSING
AND b.c.d._class IS MISSING
That will update any document that has a 'd' object within a 'c' object but doesn't have a '_class' within the c object.

Related

Map<String, HashSet<String>> to JSON, & Pretty Print

I'm trying to make my dataset correspond to this example:
var family = [{
"name" : "Jason",
"age" : "24",
"gender" : "male"
},
{
"name" : "Kyle",
"age" : "21",
"gender" : "male"
}];
I have a Map<String, HashSet<String>> of Names and unique alpha-numeric values correponding to specific entities to which those names could refer, let's call these entry items "IDs".
So for instance, Fyodor Mikhailovich Dostoyevsky would perhaps be related to the ID Q626, because that's a very specific reference, there aren't many widely known figures with that name. Whereas, Bush might be attached to G027, Q290, and Q118, referencing perhaps the man, the beer, and the shrub, in no particular order.
It looks like this (the real one is much bigger):
[Rao=[Q7293658, , Q7293657, Q12953055, Q3531237, Q4178159, Q1138810, Q579515, Q3365064, Q7293664, Q1133815], Hani Durzy=[], Louise=[, Q1660645, Q130413, Q3215140, Q152779, Q233203, Q7871343, Q232402, Q82547, Q286488, Q156723, Q3263649, Q456386, Q233192, Q14714149, Q12125864, Q57669, Q168667, Q141410, Q166028], Reyna=[Q7573462, Q2892895, Q363257, Q151944, Q3740321, Q2857439, Q1453358, Q7319529, Q733716, Q16151941, Q7159448, Q5484172, Q6074271, Q1753185, Q7319532, Q5171205, Q3183869, Q1818527, Q251862, Q3840414, Q5271282, Q5606181]]
Using Jackson I tried like this:
Map<String, HashSet<String>> map = q_valMap;
mapper.writeValue(new File("JSON_Output/user.json"), map);
But this seems wrong, as my output was all jumbled together, i.e.
{"Rao":["Q7293658","","Q7293657","Q12953055","Q3531237","Q4178159","Q1138810","Q579515","Q3365064","Q7293664","Q1133815"],"Hani Durzy":[""],"Louise":["","Q1660645","Q130413","Q3215140","Q152779","Q233203","Q7871343","Q232402","Q82547","Q286488","Q156723","Q3263649","Q456386","Q233192","Q14714149","Q12125864","Q57669","Q168667","Q141410","Q166028"],"Reyna":["Q7573462","Q2892895","Q363257","Q151944","Q3740321","Q2857439","Q1453358","Q7319529","Q733716","Q16151941","Q7159448","Q5484172","Q6074271","Q1753185","Q7319532","Q5171205","Q3183869","Q1818527","Q251862","Q3840414","Q5271282","Q5606181"]}
Do I just have to populate this JSON object iteratively?
Like the example up top, I think it should look something like this, though what follows is only a pseudocodish characterization, which is to say, not exactly this but something similar:
{
key: "Rao"
value: ["Q7293658","","Q7293657","Q12953055","Q3531237","Q4178159","Q1138810","Q579515","Q3365064","Q7293664","Q1133815"]
key: "Hani Durzy"
value: [""]
key: "Louise"
value: ["","Q1660645","Q130413","Q3215140","Q152779","Q233203","Q7871343","Q232402","Q82547","Q286488","Q156723","Q3263649","Q456386","Q233192","Q14714149","Q12125864","Q57669","Q168667","Q141410","Q166028"]
key: "Reyna"
value: ["Q7573462","Q2892895","Q363257","Q151944","Q3740321","Q2857439","Q1453358","Q7319529","Q733716","Q16151941","Q7159448","Q5484172","Q6074271","Q1753185","Q7319532","Q5171205","Q3183869","Q1818527","Q251862","Q3840414","Q5271282","Q5606181"]
}
is that not right?
UPDATE
public class JsonMapFileExample
{
public static void map(Map<String, HashSet<String>> q_valMap )
{
ObjectMapper mapper = new ObjectMapper();
ArrayNode array = mapper.createArrayNode();
for ( Entry entry: q_valMap.entrySet() )
{
ObjectNode node = mapper.createObjectNode()
.put("name", entry.getKey())
.put("ids", entry.getValue());
array.add(node);
}
mapper.writeValue("/home/matthias/Workbench/SUTD/nytimes_corpus/wdtk-parent/wdtk-examples/JSON_Output/user.json", array);
}
}
class MyEntity
{
private String name;
Set<String> value; // use names that you want in the result JSON
//constructors
public MyEntity()
{
}
public MyEntity(String name)
{
this.name = name;
}
//getters
public String getName()
{
return this.name;
}
public Set<String> getValue()
{
return this.value;
}
//setters
public void setName(String name)
{
this.name = name;
}
public void setValue(Set<String> value)
{
this.value = value;
}
}
You could manually set the key names, something like:
ArrayNode array = mapper.createArrayNode();
for (Entry entry: yourMap.entries()) {
ObjectNode node = mapper.createObjectNode()
.put("name", entry.key())
.putPOJO("ids", entry.value());
array.add(node);
}
mapper.writeValue(file, array);
Alternatively, you could create a class for your data
class MyEntity {
String name;
Set<String> ids; // use names that you want in the JSON result
// getters, setters if necessary
}
Transform your data map into a list of MyEntity, then use Jackson ObjectMapper to create JSON like mapper.writeValue(file, listOfMyEntities), the output would be like
[
{
"name": "some name here",
"ids": ["id1", "id2", ...]
}
// more elements here
]
how about this:
String name_list_file = "/home/matthias/Workbench/SUTD/nytimes_corpus/NYTimesCorpus/2005/01/02/test/people_test.txt";
String single_name;
try (
// read in the original file, list of names, w/e
InputStream stream_for_name_list_file = new FileInputStream( name_list_file );
InputStreamReader stream_reader = new InputStreamReader( stream_for_name_list_file , Charset.forName("UTF-8"));
BufferedReader line_reader = new BufferedReader( stream_reader );
)
{
while (( single_name = line_reader.readLine() ) != null)
{
//replace this by a URL encoder
//String associated_alias = single_name.replace(' ', '+');
String associated_alias = URLEncoder.encode( single_name , "UTF-8");
String platonic_key = single_name;
System.out.println("now processing: " + platonic_key);
Wikidata_Q_Reader.getQ( platonic_key, associated_alias );
}
}
//print the struc
Wikidata_Q_Reader.print_data();
}

gson flat json to nested objects needs serializer/deserializer?

I have some JSON coming in (I don't have any control or ability to change the structure and/or naming within the JSON...important to keep in mind in this question) that has a "flat" structure similar to this:
{
"name": "...",
"email": "...",
"box_background_color": "...",
"box_border_color": "...",
"box_text_color": "...",
...
}
Now, I can just create a simple object that keeps everything flat, like so:
public class Settings {
#SerializedName("name")
private String _name;
#SerializedName("email")
private String _emailAddress;
#SerializedName("box_background_color")
private String _boxBackgroundColor;
#SerializedName("box_border_color")
private String _boxBorderColor;
#SerializedName("box_text_color")
private String _boxTextColor;
...
}
However, I want everything associated with box settings to be in it's own class (BoxSettings). This is more like what I want:
public class Settings {
#SerializedName("name")
private String _name;
#SerializedName("email")
private String _emailAddress;
private BoxSettings _boxSettings
...
}
public class BoxSettings {
#SerializedName("box_background_color")
private String _boxBackgroundColor;
#SerializedName("box_border_color")
private String _boxBorderColor;
#SerializedName("box_text_color")
private String _boxTextColor;
...
}
I know that if the JSON was structured such that the box settings were nested then it would be easy to accomplish what I want, however, I don't have the ability to change the structure of the JSON, so please don't suggest that (I would do it if I could).
My question is this: Is creating an entire TypeAdapter the only way to accomplish what I want or can I still accomplish most of this with annotations? If it is not the only way, how else can I accomplish this without changing the JSON at all?
The following is an example of what I mean by "creating an entire TypeAdapter":
public class SettingsTypeAdapter implements JsonDeserializer<Settings>, JsonSerializer<Settings> {
#Override
public JsonElement serialize(Settings src, Type typeOfSrc, JsonSerializationContext context) {
// Add _name
// Add _emailAddress
// Add BoxSettings._boxBackgroundColor
// Add BoxSettings._boxBorderColor
// Add BoxSettings._boxTextColor
return jsonElement;
}
#Override
public Settings deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
// Read _name
// Read _emailAddress
// Read BoxSettings._boxBackgroundColor
// Read BoxSettings._boxBorderColor
// Read BoxSettings._boxTextColor
return settings;
}
}
The TypeAdapter is not the only way, but in this case would be the best way since you can associate the adapter with a Gson instance (or whatever library you are using) and have all your mapping code there.
Another way is to use JAVA reflection. I've used a version of the below code in my projects before but never with JSON and never with nested objects (mostly when there was no other choice or if i wanted to map a SQL result set to a Java object without calling resultSet.get... a lot of times).
This will work in this case.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.json.JSONObject;
public class Main {
public static void main(String[] args) {
try {
String json = "{\"name\": \"test name\", \"email\": \"email#email.com\", \"box_background_color\": \"red\", \"box_border_color\": \"orange\", \"box_text_color\": \"white\", \"test3_var2\":3}";
JSONObject jsonObject = new JSONObject(json);
System.out.println(jsonObject);
System.out.println();
/*
* need to parse JSON into a map of String, Object
*/
Map<String, Object> mapAll = new HashMap<String, Object>();
Iterator<String> iter = jsonObject.keys();
while (iter.hasNext()) {
String key = (String) iter.next();
Object value = jsonObject.get(key);
mapAll.put(key, value);
System.out.println(key + "::::" + value);
}
System.out.println();
/*
* use the mapper to generate the objects
*/
MyMapper<TestClass1> myMapper = new MyMapper<TestClass1>();
TestClass1 result = myMapper.mapToObject(mapAll, TestClass1.class);
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class MyMapper<T> {
#SuppressWarnings("unchecked")
public T mapToObject(Map<String, Object> flatStructure, Class<T> objectClass) {
T result = null;
Field[] fields = null;
try {
// new base object
result = objectClass.newInstance();
// get all of its fields
fields = objectClass.getDeclaredFields();
for (Field field : fields) {
// normal variable
if (field.isAnnotationPresent(MyColumn.class)) {
String variableKey = field.getAnnotation(MyColumn.class).variableKey();
setJavaFieldValue(result, field.getName(), flatStructure.get(variableKey));
}
// variable that is an object and itself has to be mapped
else if (field.isAnnotationPresent(MyInnerColumn.class)) {
String startsWith = field.getAnnotation(MyInnerColumn.class).startsWith();
// reduce the map to only have attributes that are related to this field
Map<String, Object> reducedMap = reduceMap(startsWith, flatStructure);
// make sure that there are attributes for the inner object
if (reducedMap != null) {
// map the inner object
MyMapper<T> myMapper = new MyMapper<T>();
T t2 = myMapper.mapToObject(reducedMap, (Class<T>) field.getType());
// set the mapped object to the base objecct
setJavaFieldValue(result, field.getName(), t2);
}
} else {
// no annotation on the field so ignored
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private Map<String, Object> reduceMap(String startsWith, Map<String, Object> mapToReduce) {
Map<String, Object> result = new HashMap<String, Object>();
for (Map.Entry<String, Object> entry : mapToReduce.entrySet()) {
if (entry.getKey().toLowerCase().startsWith(startsWith.toLowerCase())) {
result.put(entry.getKey(), entry.getValue());
}
}
return result.size() == 0 ? null : result;
}
private void setJavaFieldValue(Object object, String fieldName, Object fieldValue) {
try {
Field field = object.getClass().getDeclaredField(fieldName);
boolean fieldAccess = field.isAccessible();
// make the field accessible
field.setAccessible(true);
field.set(object, fieldValue);
// put it back to the way it was
field.setAccessible(fieldAccess);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*
* Annotation for a regular variable / field
*/
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#interface MyColumn {
// the variable's JSON key
String variableKey() default "";
}
/*
* Annotation for an inner / nested variable / field
*/
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#interface MyInnerColumn {
/*
* JSON keys that start with this string will be
* associated with this nested field
*/
String startsWith() default "";
}
class TestClass1 {
#MyColumn(variableKey = "name")
private String _name;
#MyColumn(variableKey = "email")
private String _emailAddress;
#MyInnerColumn(startsWith = "box_")
private TestClass2 innerClass;
#MyInnerColumn(startsWith = "test3_")
private TestClass3 innerClass2;
#Override
public String toString() {
return "TestClass1 [_name=" + _name + ", _emailAddress=" + _emailAddress + ", innerClass=" + innerClass + ", innerClass2=" + innerClass2 + "]";
}
}
class TestClass2 {
#MyColumn(variableKey = "box_background_color")
private String _boxBackgroundColor;
#MyColumn(variableKey = "box_border_color")
private String _boxBorderColor;
#MyColumn(variableKey = "box_text_color")
private String _boxTextColor;
#Override
public String toString() {
return "TestClass2 [_boxBackgroundColor=" + _boxBackgroundColor + ", _boxBorderColor=" + _boxBorderColor
+ ", _boxTextColor=" + _boxTextColor + "]";
}
}
class TestClass3 {
#MyColumn(variableKey = "test3_var1")
private String _test3Var1;
#MyColumn(variableKey = "test3_var2")
private int _test3Var2;
#Override
public String toString() {
return "TestClass3 [_test3Var1=" + _test3Var1 + ", _test3Var2=" + _test3Var2 + "]";
}
}
Output
{"box_background_color":"red","box_text_color":"white","test3_var2":3,"name":"test name","email":"email#email.com","box_border_color":"orange"}
box_background_color::::red
box_text_color::::white
test3_var2::::3
name::::test name
email::::email#email.com
box_border_color::::orange
TestClass1 [_name=test name, _emailAddress=email#email.com, innerClass=TestClass2 [_boxBackgroundColor=red, _boxBorderColor=orange, _boxTextColor=white], innerClass2=TestClass3 [_test3Var1=null, _test3Var2=3]]

Array retrieval from JSON file

I have an array in JSON file which look like this
{ "fields": [
{
"name": "order_id",
"type": "INTEGER",
"position": 0
},
{
"name": "district_id",
"type": "INTEGER",
"position": 1
}]
}
I'm using a TREE MODEL in retrieving the contents of the array "fields" and my code looks like this ..
public static void main(String[] args) throws JsonParseException, IOException {
File jsonFile = new File("metadata.json");
String theJsonString = jsonFile.toString();
String name = null;
String type =null;
int position = 0;
ObjectMapper mapper = new ObjectMapper(); // can reuse, share globally
JsonNode rootNode = mapper.readTree(theJsonString);
JsonNode fields = rootNode.get("fields");
if (fields != null) {
for (int i = 0; i < fields.size(); i ++) {
if(fields.has("name"))
name = fields.get("name").getTextValue();
if(fields.has("type"))
type = fields.get("type").getTextValue();
if(fields.has("position"))
position = fields.get("position").getIntValue();
System.out.println(name);
}
}
}
I get the following error during the run time ::
Exception in thread "main" org.codehaus.jackson.JsonParseException: Unexpected character ('/' (code 47)): maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)
at [Source: java.io.StringReader#3eed2cab; line: 1, column: 2]
at org.codehaus.jackson.JsonParser._constructError(JsonParser.java:1432)
at org.codehaus.jackson.impl.JsonParserMinimalBase._reportError(JsonParserMinimalBase.java:385)
at org.codehaus.jackson.impl.JsonParserMinimalBase._reportUnexpectedChar(JsonParserMinimalBase.java:306)
at org.codehaus.jackson.impl.ReaderBasedParser._skipComment(ReaderBasedParser.java:1498)
at org.codehaus.jackson.impl.ReaderBasedParser._skipWSOrEnd(ReaderBasedParser.java:1474)
at org.codehaus.jackson.impl.ReaderBasedParser.nextToken(ReaderBasedParser.java:362)
at org.codehaus.jackson.map.ObjectMapper._initForReading(ObjectMapper.java:2761)
at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2709)
at org.codehaus.jackson.map.ObjectMapper.readTree(ObjectMapper.java:1533)
at metadata.JSONParser.main(JSONParser.java:32)
I have just started working with JSON and hence unable to find a solution. Could anybody help me resolve this?
If you want the contents of the file metadata.json then calling toString on the file will not give you that. Instead it will give you a string which holds the path to the filename.
Instead create a FileInputStream from the File like so:
FileInputStream fis = new FileInputStream(jsonFile);
The you can use it with the mapper
JsonNode rootNode = mapper.readTree(fis);
You might also want to call fields.get(i) when you are iterating through the array to access each JsonNode contained in the array.
I have the same problem and I fixed the problem finally by this code to get the JsonParser object.
FileInputStream fis = new FileInputStream(path);
JsonParser jp = new JsonFactory().createParser(fis);
I had the same problem few days ago. My JSON have the next syntax:
{ "boxes": [
{
"name": "redBox.png",
"version": 15
},
{
"name": "blueBox.png",
"version": 9
}
]
}
I had to create method which return me POJO classes with box versions. My solution is follow:
private static List<DataVersion> receiveDataVersions() throws IOException
{
String path = System.getProperty( "user.dir" ) + File.separator +
"versions.json";
List<DataVersion> versions = new ArrayList<>();
JsonNode folder = null;
try( InputStream stream = new FileInputStream( path ) )
{
folder = new ObjectMapper().readTree( stream );
}
JsonNode boxes = folder.get( "inst" );
boxes.forEach( version -> versions.add( new DataVersion(
version.get( "name" ).getTextValue(),
version.get( "version" ).getIntValue() ) ) );
return versions;
}
POJO is as follows:
public class DataVersion
{
private String name;
private int version;
public DataVersion() {
}
public DataVersion(String name, int version) {
this.name = name;
this.version = version;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getVersion() {
return version;
}
public void setVersion(int version){
this.version=version;
}
#Override
public String toString()
{
return "DataVersion [" + "name='" + name + '\'' + ", version=" +
version + ']';
}
}
try this in your code
jsonFactory.enable(JsonParser.Feature.ALLOW_COMMENTS);

Grails - how to let a domain class convert JSON into a domain property

I want to teach my domain class to automatically convert the results of JSON.parse(someJSON) into a member that is also a custom domain class.
Given these domain classes:
class Person {
Long id
String name
static hasMany = [aliases: PersonAlias]
}
class PersonAlias {
Person person
Long id
String name
}
And this JSON representing a Person with some PersonAliases:
{
"id":20044397,
"name":"John Smith",
"aliases":[{"id":13376,"name":"Johnny Smith"},{"id":13377,"name":"J. Smith"}]
}
I want to keep the controller simple like:
class PersonController {
def saveViaAjax = {
def props = JSON.parse(params.JSON)
Person p = Person.get(props.id)
p.properties = props
p.save(flush: true)
}
}
But sadly I get this error:
Failed to convert property value of type
'org.codehaus.groovy.grails.web.json.JSONArray' to required type
'java.util.Set' for property 'aliases'; nested exception is
java.lang.IllegalStateException: Cannot convert value of type
[org.codehaus.groovy.grails.web.json.JSONObject] to required type
[heavymeta.PersonAlias] for property 'aliases[0]': no matching editors
or conversion strategy found
So, I want to teach my domain class to how to convert the JSON data into PersonAlias instances automatically. I'd like to avoid formatting the data in the controller before passing it to the Domain object. How do I accomplish these goals?
You can use the bindUsing annotation and provide your custom binding code to convert the json to the property being bound.
class Person {
Long id
String name
#BindUsing({obj, source ->
List retVal = []
def aliases = source['aliases']
if(aliases) {
aliases.each {
retVal << new PersonAlias(name:it.name)
}
}
return retVal
})
List<PersonAlias> aliases
static hasMany = [aliases: PersonAlias]
}
I think this plugin: https://github.com/pedjak/grails-marshallers might do what you're looking for? I have not tried it myself though.
I also encountered this problem - I did my best to document the fix on my website - See http://dalelotts.com/software-architect/grails
In general the solution is to convert the JSON to a parameter map that can be used for data binding. More info on the site, including an annotation driven DomainClassMarshaller for JSON
protected Object readFromJson(Class type, InputStream entityStream, String charset) {
def mapper = new ObjectMapper();
def parsedJSON = mapper.readValue(entityStream, typeRef);
Map<String, Object> map = new HashMap<>();
parsedJSON.entrySet().each {Map.Entry<String, Object> entry ->
if (List.isAssignableFrom(entry.getValue().getClass())) {
List values = (List) entry.getValue();
int limit = values.size()
for (int i = 0; i < limit; i++) {
final theValue = values.get(i)
map.put(entry.key + '[' + i + ']', theValue)
appendMapValues(map, theValue, entry.key + '[' + i + ']' )
}
} else {
map.put(entry.key, entry.value);
}
}
def result = type.metaClass.invokeConstructor(map)
// Workaround for http://jira.codehaus.org/browse/GRAILS-1984
if (!result.id) {
result.id = idFromMap(map)
}
result
}
private void appendMapValues(Map<String, Object> theMap, Object theValue, String prefix) {
if (Map.isAssignableFrom(theValue.getClass())) {
Map<String, Object> valueMap = (Map<String, Object>) theValue;
for (Map.Entry<String, Object> valueEntry : valueMap.entrySet()) {
theMap.put(prefix + '.' + valueEntry.key, valueEntry.value)
appendMapValues(theMap, valueEntry.value, prefix + '.' + valueEntry.key)
}
}
}

Datanucleus JDO setting fields to null

In an attempt to find another issue, my tests came up with the following bit of code.
public class TestPersistance {
private static final PersistenceManagerFactory PMF = JDOHelper.getPersistenceManagerFactory("datanucleus.properties");
public static final PersistenceManager pm = PMF.getPersistenceManager();
static final TestUserDataDB ud = new TestUserDataDB();
public static void main(String args[])
{
TestPersistance tp = new TestPersistance();
tp.createData();
}
#Test public void createData()
{
assertTrue("Null machined id at start", ud.machineId != null);
pm.currentTransaction().begin();
try
{
pm.makePersistent(ud);
}
finally
{
pm.currentTransaction().commit();
}
assertTrue("Null machined id at end", ud.machineId != null);
}
}
where the second assert fails. ie. my object that I am asking to be persisted is being changed by the makePersistent call. The data is being stored in the database.
Any ideas? Can any one confirm this.
using
jdo-api-3.0.jar
datanucleus-core-2.2.0-release.jar
datanucleus-enhancer-2.1.3.jar
datanucleus-rdbms-2.2.0-release.jar
mysql-connector-java-5.1.13.jar
in eclipse with MySql database.
#PersistenceCapable
public class TestUserDataDB {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
public Long id;
#Persistent
public String userid = "test1";
#Persistent
public String machineId = "test2";
// local userid
#Persistent
public long uid = 1L;
#Persistent
public long systemTime = 123L;
public long chk = 1234L;
public long createTime = System.currentTimeMillis();
public TestUserDataDB()
{
}
#Override
public String toString() {
return "TestUserDataDB [chk=" + chk + ", createTime=" + createTime
+ ", id=" + id + ", machineId=" + machineId + ", systemTime="
+ systemTime + ", uid=" + uid + ", userid=" + userid + "]";
}
}
Properties file is
javax.jdo.PersistenceManagerFactoryClass=org.datanucleus.jdo.JDOPersistenceManagerFactory
datanucleus.metadata.validate=false
javax.jdo.option.ConnectionDriverName=com.mysql.jdbc.Driver
javax.jdo.option.ConnectionURL=jdbc:mysql://localhost/test
javax.jdo.option.ConnectionUserName=root
javax.jdo.option.ConnectionPassword=yeahRight
datanucleus.autoCreateSchema=true
datanucleus.validateTables=false
datanucleus.validateConstraints=false
Why are you accessing fields directly ? Is the accessing class declared as PersistenceAware ? Well it isn't so you can't do that - use the getters.
What is "ud" object state before persist ? (transient?) what is it after persist ? (hollow?) What does the log say ? Chances are that it is in hollow state and then you access a field directly and it has no value (by definition, as per the spec) ... but since you didn't bother calling the getter it hasn't a chance to retrieve the value. And you likely also don't have "RetainValues" persistent property set
Suggest you familiarise yourself with the JDO spec and object lifecycle states
In some cases, it is necessary to access deserialized objects' attributes directly (i.e. if using GSON library for JSON serialization). In that case you can use:
MyClass copy = myPersistencyManager.detachCopy(myRetrievedInstance);