I am using apache CXF.
The following API is used to post a Contact.
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
ResponseResult create(#Context HttpHeaders httpHeaders, #Context Request request, #Context UriInfo uriInfo,
UserContact contact) throws MDMException;
Here the UserContact class contains the contact information about a user which is passed as JSON in the body.
I need to do some business validations on this UserContact object. But I do not like to have too much validation code packed in a single class.
I would like to do something like the following. But I am facing issue with Generics.
interface Rule<S>
{
void applyRule(S s)throws Exception;
}
interface Validatable
{
void validate() throws Exception;
}
public class MyValidator
{
private HashMap<? extends Rule ,?> map = new HashMap<>();
public void validate() throws Exception
{
for(Rule rule : map.keySet())
{
rule.applyRule(map.get(rule));
}
}
public <S> void addRule(Rule<S> rule, S data)
{
this.map.put(rule, data);
}
}
class EMailValidationRule implements Rule<String>
{
private static final Pattern emailPattern = Pattern.compile("email-regex");
public void applyRule(String s) throws Exception
{
if(!emailPattern.matcher(s).matches())
throw new Exception("Not a valid EMail");
}
}
So the UserContact has to do the following for its validation purposes. This keeps the code compact (IMO).
class UserContact implements Validatable
{
// some
// code
// related to User Contact
public void validate() throws Exception
{
MyValidator validator = new MyValidator();
validator.addRule(new EMailValidationRule(), "developer#stackoverflow.com");
validator.addRule(new PhoneValidationRule(), "+1234567890");
validator.validate();
}
}
I keep getting error like :
The method put(capture#5-of ? extends Rule, capture#6-of ?) in the type HashMap is not applicable
for the arguments (Rule, S)
Also is the above design good for doing validations?
The problem is that, although your encapsulation ensures it, the compiler can not be sure that the retrieved Rule<...> has a type argument of the same type as the retrieved data.
There is also the problem of not being able to insert a Rule<T> with data of a subtype of T. If you have Rule<S> rule, S data the types have to be an exact match. While a Rule<S> could handle a subtype of S just fine.
While MyValidator is a cool little class, I can't really see the point in having it. Especially because you create a new one every time you call validate. It would also be hard to cache because the rules are static (the same for every instance of the class) and the data comes from individual instances (I'd assume).
You could also just do this:
class UserContact implements Validatable
{
// some
// code
// related to User Contact
// 1 rule instance for the entire class, not a new one per call to 'validate'
private static EMailValidationRule emailRule = new EmailValidationRule();
private static PhoneValidationRule phoneRule = new PhoneValidationRule();
public void validate() throws Exception
{
emailRule.applyRule("developer#stackoverflow.com");
phoneRule.applyRule("+1234567890");
}
}
Never the less, here is a working version of MyValidator:
class MyValidator {
private Map<Rule<?>, RuleNode<?>> map = new HashMap<>();
public void validate() throws Exception {
for(RuleNode<?> node : map.values())
node.apply();
}
public <T, D extends T> void addRule(Rule<T> rule, D data) {
#SuppressWarnings("unchecked") // unchecked, but safe due to encapsulation
RuleNode<T> r = (RuleNode<T>) map.get(rule);
if(r == null) {
r = new RuleNode<T>(rule);
map.put(rule, r);
}
r.add(data);
}
private static class RuleNode<T> { // Maintains that the rule and data are compatible
private final Rule<T> rule;
private final List<T> list = new ArrayList<>();
public RuleNode(Rule<T> rule) {
this.rule = rule;
}
public void add(T data) {
list.add(data);
}
public void apply() throws Exception {
for(T data : list)
rule.applyRule(data);
}
}
}
You just need to make a generic Version of the MyValidator Class
A generic class is defined with thss format:
class name<T1, T2, ..., Tn> { /* ... */ }
Defining the class using generics you will specify the types you want to use in your class, in your case <R extends Rule<S> ,S>
class MyValidator<R extends Rule<S> ,S>{
private HashMap<R ,S> map = new HashMap<>();
public void validate() throws Exception{
for(Rule<S> rule : map.keySet()){
rule.applyRule(map.get(rule));
}
}
public void addRule(R rule, S data){
this.map.put(rule, data);
}
}
Once done you just have to build a MyValidator of the desired type :
MyValidator<Rule<String>, String> validator = new MyValidator<>();
And finally add the rules matching the types of the validator :
validator.addRule(new EMailValidationRule(), "developer#stackoverflow.com");
So for example addind a phone validator your UserContact will looks like :
class PhoneValidationRule implements Rule<String>{
private static final Pattern phonePattern = Pattern.compile("phone-regex");
public void applyRule(String s) throws Exception{
if(!phonePattern.matcher(s).matches())
throw new Exception("Not a valid phone");
}
}
class UserContact implements Validatable{
public void validate() throws Exception{
MyValidator<Rule<String>, String> validator = new MyValidator<>();
validator.addRule(new EMailValidationRule(), "developer#stackoverflow.com");
validator.addRule(new PhoneValidationRule(), "developer#stackoverflow.com");
validator.validate();
}
}
Related
I have a class that loaded data from scenario steps
my first class is LoadUserStepDfn
public class LoadUserStepDfn extends LoadDataStepDfn<User> {
public LoadUserStepDfn(ReadingUserUsingPoiji readingUserUsingPoiji) {
super.readingExcelUsingPoiji = readingUserUsingPoiji;
}
#Given("^Data is loaded from \"([^\"]*)\"$")
public void data_is_loaded_from (String filePath) throws Throwable {
super.data_is_loaded_from(filePath);
}
and it call class named LoadDataStepDfn
public class LoadDataStepDfn<T> {
public List<T> data;
protected ReadingExcelUsingPoiji readingExcelUsingPoiji;
public void data_is_loaded_from (String filePath) throws Throwable {
data = readingExcelUsingPoiji.TransformExcelToClass(filePath);
}
and here is my class that reads excel and store it to java class
public abstract class ReadingExcelUsingPoiji<T> {
public List<T> TransformExcelToClass(String filePath){
PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().addListDelimiter(";").build();
List<T> data = Poiji.fromExcel(new File(filePath), getMyType(), options);
return data;
}
public abstract Class<T> getMyType();
}
the problem that I want to use one class I don't want it to be abstract and use another one wiche is this class
public class ReadingUserUsingPoiji extends ReadingExcelUsingPoiji<User> {
public Class<User> getMyType(){
return User.class;
}
I am trying to understand here, so you dont want #override, but rather 1 method that returns you the type of class to transform to??
Why can't it be that simple... You have a method that determines what class you should use to transform to...
I dont understand why you are using generics...your logic doesnt seem to really care for it? Especially if you have 1 ReadingExcelUsingPoiji class..it really shouldnt care.
public class ReadingExcelUsingPoiji<T> {
public List<T> transformExcelToClass(String filePath, Class<T> classToTransformTo) {
PoijiOptions options = PoijiOptions.PoijiOptionsBuilder.settings().addListDelimiter(";").build();
List<T> data = Poiji.fromExcel(new File(filePath), classToTransformTo, options);
return data;
}
public static void main(String [] args) {
ReadingExcelUsingPoiji genericConverter = new ReadingExcelUsingPoiji();
List<User> listOfUsers = genericConverter.transformExcelToClass("yourFilePath", User.class);
List<Car> listOfCars = genericConverter.transformExcelToClass("yourFilePath", Car.class);
}
}
public class LoadUserStepDfn extends LoadDataStepDfn<User> {
#Given("^Data is loaded from \"([^\"]*)\"$")
public void data_is_loaded_from (String filePath) throws Throwable {
super.data_is_loaded_from(filePath , User.class);
}
}
public class LoadDataStepDfn<T> {
public List<T> data;
protected ReadingExcelUsingPoiji readingExcelUsingPoiji;
protected void data_is_loaded_from(String filePath, Class<T> classToTransformTo) throws Throwable {
data = readingExcelUsingPoiji.transformExcelToClass(filePath, classToTransformTo);
}
}
Problem statement:
I have to process request similar to a pipeline.
For example:
When a request comes, it has to undergo a sequence of operations, like (step1,step2,step3...).
So, in order to achieve that, I am using Template design pattern.
Please review and suggest if I am implementing this problem correctly, or there is a better solution.
I am suspecting my approach will introduce code smells, as I am changing values of objects very frequently.
Also, suggest if I & how can I use Java 8 to accomplish this?
Thanks.
Code:
package com.example.demo.design;
import java.util.List;
public abstract class Template {
#Autowired
private Step1 step1;
#Autowired
private Step2 step2;
#Autowired
private Save save;
List<String> stepOutput = null;
List<String> stepOutputTwo = null;
List<String> stepOutputThree = null;
public void step1(String action1) {
stepOutput = step1.method(action1);
}
public void step2(String action2) {
stepOutputTwo = step2.method(stepOutput, action2);
}
abstract public void step3();
public void save() {
save.persist(stepOutputThree);
}
final public void run(String action1, String action2) {
step1(action1);
step2(action2);
stepOutputTwo = step3();
}
}
In Java 8 streams model, that could look like the following:
final public void run(String action1, String action2) {
Stream.of(action1) // Stream<String>
.map(s -> step1.method(s)) // Stream<List<String>>
.map(l -> step2.method(l,action2) // Stream<List<String>>
.map(l -> step3.method(l)) // Stream<List<String>>
.forEach(l -> save.persist(l));
}
I had same issue! you can do something like this: and uncheckCall method is for handling exceptions.
final public void run(String action1, String action2) {
//other stuffs
Stream.of(step1.method(action1))
.map(stepOutput->uncheckCall(() ->step2.method(stepOutput,action2)))
.forEach(stepOutputThree -> uncheckCall(()->save.persist(stepOutputThree)));
//.....
}
for uncheckCall method:
public static <T> T uncheckCall(Callable<T> callable) {
try {
return callable.call();
} catch (RuntimeException e) {
// throw BusinessException.wrap(e);
} catch (Exception e) {
//throw BusinessException.wrap(e);
}
}
Well, when there are "pipelines", "sequence of operations", etc. the first design pattern that comes to mind is Chain of Responsibility, that looks like the following
and provides you with these benefits:
allows you to add new handlers when necessary (e.g. at runtime) without modifying other handlers and processing logic (Open/Closed Principle of SOLID)
allows a handler to stop processing a request if necessary
allows you to decouple processing logic of the handlers from each other (Single Responsibility Principle of SOLID)
allows you to define the order of the handlers to process a request outside of the handlers themselves
One example of real world usage is Servlet filters where you call doFilter(HttpRequest, HttpResponse, FilterChain) to invoke the next handler
protected void doFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) {
if (haveToInvokeNextHandler) {
chain.doFilter(req, resp);
}
}
In case of using classical Chain of Responsibility pattern your processing pipeline may look like the following:
API
public class StepContext {
private Map<String, Object> attributes = new HashMap<>();
public <T> T getAttribute(String name) {
(T) attributes.get(name);
}
public void setAttribute(String name, Object value) {
attributes.put(name, value);
}
}
public interface Step {
void handle(StepContext ctx);
}
public abstract class AbstractStep implements Step {
private Step next;
public AbstractStep() {
}
public AbstractStep(Step next) {
this.next = next;
}
protected void next(StepContext ctx) {
if (next != null) {
next.handle(ctx);
}
}
}
Implementation
public class Step1 extends AbstractStep {
public Step1(Step next) {
super(next);
}
public void handle(StepContext ctx) {
String action1 = ctx.getAttribute("action1");
List<String> output1 = doSomething(action1);
ctx.setAttribute("output1", output1);
next(ctx); // invoke next step
}
}
public class Step2 extends AbstractStep {
public Step2(Step next) {
super(next);
}
public void handle(StepContext ctx) {
String action2 = ctx.getAttribute("action2");
List<String> output1 = ctx.getAttribute("output1");
List<String> output2 = doSomething(output1, action2);
ctx.setAttribute("output2", output2);
next(ctx); // invoke next step
}
}
public class Step3 extends AbstractStep {
public Step3(Step next) {
super(next);
}
public void handle(StepContext ctx) {
String action2 = ctx.getAttribute("action2");
List<String> output2 = ctx.getAttribute("output2");
persist(output2);
next(ctx); // invoke next step
}
}
Client code
Step step3 = new Step3(null);
Step step2 = new Step2(step3);
Step step1 = new Step1(step2);
StepContext ctx = new StepContext();
ctx.setAttribute("action1", action1);
ctx.setAttribute("action2", action2);
step1.handle(ctx);
Also all this stuff can be simplified into a chain of handlers decoupled from each other by means of removing the corresponding next references in case your processing pipeline will have to always invoke all the available steps without controlling the necessity of invocation from the previous one:
API
public class StepContext {
private Map<String, Object> attributes = new HashMap<>();
public <T> T getAttribute(String name) {
(T) attributes.get(name);
}
public void setAttribute(String name, Object value) {
attributes.put(name, value);
}
}
public interface Step {
void handle(StepContext ctx);
}
Implementation
public class Step1 implements Step {
public void handle(StepContext ctx) {
String action1 = ctx.getAttribute("action1");
List<String> output1 = doSomething(action1);
ctx.setAttribute("output1", output1);
}
}
public class Step2 implements Step {
public void handle(StepContext ctx) {
String action2 = ctx.getAttribute("action2");
List<String> output1 = ctx.getAttribute("output1");
List<String> output2 = doSomething(output1, action2);
ctx.setAttribute("output2", output2);
}
}
public class Step3 implements Step {
public void handle(StepContext ctx) {
String action2 = ctx.getAttribute("action2");
List<String> output2 = ctx.getAttribute("output2");
persist(output2);
}
}
Client code
Note that in case of Spring framework (just noticed #Autowired annotation) the client code may be simplified even more as the #Autowired annotation can be used to inject all the beans of the corresponding type into a corresponding collection.
Here what the documentation states:
Autowiring Arrays, Collections, and Maps
In case of an array, Collection, or Map dependency type, the container autowires all beans matching the declared value type. For such purposes, the map keys must be declared as type String which will be resolved to the corresponding bean names. Such a container-provided collection will be ordered, taking into account Ordered and #Order values of the target components, otherwise following their registration order in the container. Alternatively, a single matching target bean may also be a generally typed Collection or Map itself, getting injected as such.
public class StepsInvoker {
// spring will put all the steps into this collection in order they were declared
// within the spring context (or by means of `#Order` annotation)
#Autowired
private List<Step> steps;
public void invoke(String action1, String action2) {
StepContext ctx = new StepContext();
ctx.setAttribute("action1", action1);
ctx.setAttribute("action2", action2);
steps.forEach(step -> step.handle(ctx))
}
}
I apologize if this has been answered before but either i don't know the correct verbiage or my google fu is bad.
I have a TestModel class which has the getters and setters for all the tests I use. Then I have a AdditionalTestModel class that extends the TestModel with additional getters and setters for that specific type of tests.
Now I have BuildTest Class that i want to be able to pass TestModel and any extended classes of TestModel.
public static Class<?> buildTest(Class<?> test, Class<?> template)
throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Class<?> testClass = test.getClass();
Method[] testMethods = testClass.getMethods();
for (Method method : testMethods) {
String name = method.getName();
if (name.startsWith("get")) {
String testMethodType = method.getReturnType().getTypeName();
// additional code removed//
}
}
If instead of Class<?> i was using TestModel it would work for any test that i pass of Class type TestModel. But i want to be able to pass the extended class to this method as well without having to write a method for each extended class. Any recommendations?
Adding information on the models in case it matters.
public class TestModel {
private String testDescription;
private String testName;
private String apiPath;
private String method;
private String expectedTest;
private Map<String, String> header = new HashMap<>();
private Object body;
private String expectedResult;
private String testCaseId;
private String testUUID;
private List testTypes;
public String getTestDescription() {
return testDescription;
}
public void setTestDescription(String testDescription) {
this.testDescription = testDescription;
}
public String getTestName() {
return testName;
}
public void setTestName(String testName) {
this.testName = testName;
}
public String getAPIPath() {
return apiPath;
}
public void setAPIPath(String apiPath) {
this.apiPath = apiPath;
}
public String getExpectedTest() {
return expectedTest;
}
public void setExpectedTest(String testName) {
this.expectedTest = testName;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public Map<String, String> getHeader() {
return header;
}
public void setHeader(Map<String, String> header) {
this.header = header;
}
public Object getBody() {
return body;
}
public void setBody(Object body) {
this.body = body;
}
public String getExpectedResult() {
return expectedResult;
}
public void setExpectedResult(String expectedResult) {
this.expectedResult = expectedResult;
}
public String getTestCaseId() {
return testCaseId;
}
public void setTestCaseId(String testCaseId) {
this.testCaseId = testCaseId;
}
public String getTestUUID() {
return testUUID;
}
public void setTestUUID(String testUUID) {
this.testUUID = testUUID;
}
public List getTestTypes() {
return testTypes;
}
public void setTestTypes(List testTypes) {
this.testTypes = testTypes;
}
}
public class AdditionalTestModel extends TestModel {
#Override public Object getBody() {
return super.getBody();
}
}
Edit: per a request adding the call information here:
#Test(dataProvider = "Default", threadPoolSize = THREADS, timeOut = API_TIME_OUT)
#Description("")
public void sampleTest(AdditionalTestModel testFromDataProvider) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
testSetup(testFromDataProvider);
AdditionalTestModel test = BuildTest.buildTest(testFromDataProvider, template);
Response response = RestAPI.call(test, testEnvironment);
if (null != response) {
ValidateAPIResponse.validateTestModel(test, response);
} else {
Assert.fail("Response is null, probably a bad method.");
}
}
Where testFromDataProvider is passed from a TestNg data provider.
Now LppEdd below already pointed out i could only assign the base class using generics so working on trying it his way, just have not gotten a chance to change things up yet.
Edit: Also realize now my question was bad. Thanks LppEdd. I should have asked How can I get a method to accept an instance of a class and an instance of any extended class
You are close, you just need to use the extends modifier.
If the class passed in as the test and template parameter should be the same exact class type, you can do:
public static <T extends TestModel> Class<T> buildTest(Class<T> test, Class<T> template) { ... }
Otherwise you can do
public static Class<? extends extends TestModel> buildTest(Class<? extends TestModel> test, Class<? extends String> extends TestModel) { ... }
Which will allow different types to be returned and passed in to each parameter.
You can read up on Java generics and wilcards starting here: https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html
Your buildTest method must accept a TestModel class.
You might be looking for something like
public static TestModel buildTest(
final TestModel test,
final TestModel template) {
final Class<? extends TestModel> testClass = test.getClass();
final Method[] testMethods = testClass.getMethods();
for (final Method method : testMethods) {
final String name = method.getName();
if (name.startsWith("get")) {
final String testMethodType = method.getReturnType().getTypeName();
// additional code removed
}
}
// Maybe
return yourNewInstance; // yourNewInstance is a TestModel, or any class extending it
}
The template argument seems unused here (clarify).
What's the wanted return type? (clarify)
Usage example
final TestModel value1 = buildTest(new TestModel(), ...);
final TestModel value2 = buildTest(new AdditionalTestModel(), ...);
This looks to be exactly the same problem as must be solved by test frameworks. For example, see junit (https://junit.org/junit5/).
The core problem is how to obtain the collection of test methods of a class.
A direct solution would be to have the test class be required to answer its test methods, say, Collection<Function<Void, Void>> getTests(); This has several problems, one being that sub-classes must explicitly list their test methods, two being that sub-classes must be careful to add in the test methods from their super-class, and third, this really fits more as static behavior, which would try to shift java instance typing to the class layer, which just isn't supported by java.
An indirect solution would be to require that test methods satisfy a particular pattern (for example, must start with "test" and have no parameters), and use reflection to discover the methods. Or, use an annotation (say, #Test, which is what junit does) to mark out test methods, and again use the java reflection API to discover methods with the marker.
I'm looking for the proper way to implement generics (or some better solution with data structures) for the following four classes. I want to be able to use the method in the base class GetActivationResponse. The idea was to separate plain Java sources from sources which were dependent on the Android platform.
GetActivationResponse:
public class GetActivationResponse extends ResponseMessage {
protected List<Activation> mActivations;
public GetActivationResponse(JSONObject jsonObject) throws JSONException {
super(jsonObject);
mActivations = new ArrayList<>();
fromJson(jsonObject);
}
#Override
public void fromJson(JSONObject jsonObject) throws JSONException {
//get the response DATA.
if(jsonObject.has("DATA")) {
JSONArray dataArray = jsonObject.getJSONArray("DATA");
for(int i = 0; i < dataArray.length(); i++) {
mActivations.add(new Activation(dataArray.getJSONObject(i)));
}
}
}
}
GetActivationResponseAndroid:
public class GetActivationResponseAndroid extends GetActivationResponse implements Cacheable {
public GetActivationResponseAndroid(JSONObject jsonObject) throws JSONException {
super(jsonObject);
}
#Override
public List<ContentValues> getContentValuesList() {
List<ContentValues> values = new ArrayList<ContentValues>();
for(int i = 0; i < mActivations.size(); i++) {
values.add(((ActivationAndroid)(mActivations.get(i))).toContentValues());
}
return values;
}
}
I get a ClassCastException here, though:
((ActivationAndroid)(mActivations.get(i))).toContentValues()
ActivationAndroid is a subclass of Activation. My goal is to be able to use any subclass of Activation as well as Activation itself. I think this might involve generics. I've tried changing the List<Activation> mActivations to List<? extends Activation> mActivations, but then the compiler tells me that the line in GetActivationResponse which fills the List has an error on this line:
mActivations.add(new Activation(dataArray.getJSONObject(i)));
Apparently <? extends Activation> means you can ONLY use subclasses, but not the base class itself. I want to have the fromJSON method in the base class, seeing as how it works the same with Activation or any of its subclasses.
ActivationAndroid is a subclass of Activation which provides a convenience method for converting the object into a List<ContentValues> for use with Android database transactions:
Activation:
public class Activation {
protected String mApp;
protected boolean mActivated;
public Activation(String app, boolean activated) {
mApp = app;
mActivated = activated;
}
public Activation(JSONObject json) throws JSONException {
mApp = json.getString("APP");
mActivated = json.getBoolean("ACTIVATED");
}
}
ActivationAndroid:
public class ActivationAndroid extends Activation implements AndroidSerial {
public ActivationAndroid (String app, boolean activated) {
super(app, activated);
}
public ActivationAndroid (JSONObject jsonScale) throws JSONException {
super(jsonScale);
}
public ContentValues toContentValues() {
ContentValues cv = new ContentValues();
cv.put(DbSchema.ActivationSchema.COLUMN_ACTIVATED, mActivated);
cv.put(DbSchema.ActivationSchema.COLUMN_APP, mApp);
return cv;
}
}
I would like to store some field of type ParentClass as json string into my database. I don't want to use Serializable interface and DataType.SERIALIZABLE cause it ties with full class name of serialized class.
So I'm using the following code:
class ParentClass {
#DatabaseField(persisterClass = MyFieldClassPersister.class)
private MyFieldClass myField;
}
where persister class a kind of:
public class MyFieldClassPersister extends StringType {
private static final MyFieldClassPersister singleTon = new MyFieldClassPersister();
public static MyFieldClassPersister getSingleton() {
return singleTon;
}
protected MyFieldClassPersister() {
super(SqlType.STRING, new Class<?>[0]);
}
#Override
public Object parseDefaultString(FieldType fieldType, String defaultStr) {
return jsonStringToObject(defaultStr);
}
#Override
public Object resultToSqlArg(FieldType fieldType, DatabaseResults results, int columnPos) throws SQLException {
String string = results.getString(columnPos);
return jsonStringToObject(string);
}
private static MyFieldClass jsonStringToObject(String string) {
// json to object conversion logic
}
}
Here are two issues I've met:
I didn't get how to specify custom convertion from object to string. Seems that ORMLite calls Object.toString() in order to get string representation of the object. It would be great to have some method in Persister in which I could specify how to convert Object to string (json in my case). Yes, I can override toString() method in MyFieldClass, but it is more convenient to perform conversion in Persister. Is there any method I could override in order to specify convertion from model object to db-object?
If I mark my custom field type as String type:
class ParentClass {
#DatabaseField(dataType = DataType.STRING, persisterClass = MyFieldClassPersister.class)
private MyFieldClass myField;
}
then ormlite crashes when saving object with the following message:
java.lang.IllegalArgumentException: Field class com.myapp.venue.MyFieldClass for
field FieldType:name=myField,class=ParentClass is not valid for type
com.j256.ormlite.field.types.StringType#272ed83b, maybe should be
class java.lang.String
It doesn't crash if I omit dataType specification. Can I avoid this crash in some way? It seems to me that it's better to specify types explicitly.
So basically your persister should be implemented in the next way:
public class MyFieldClassPersister extends StringType {
private static final MyFieldClassPersister INSTANCE = new MyFieldClassPersister();
private MyFieldClassPersister() {
super(SqlType.STRING, new Class<?>[] { MyFieldClass.class });
}
public static MyFieldClassPersister getSingleton() {
return INSTANCE;
}
#Override
public Object javaToSqlArg(FieldType fieldType, Object javaObject) {
MyFieldClass myFieldClass = (MyFieldClass) javaObject;
return myFieldClass != null ? getJsonFromMyFieldClass(myFieldClass) : null;
}
#Override
public Object sqlArgToJava(FieldType fieldType, Object sqlArg, int columnPos) {
return sqlArg != null ? getMyFieldClassFromJson((String) sqlArg) : null;
}
private String getJsonFromMyFieldClass(MyFieldClass myFieldClass) {
// logic here
}
private MyFieldClass getMyFieldClassFromJson(String json) {
// logic here
}
}
You should register it in onCreate method of your OrmLiteSqliteOpenHelper class
#Override
public void onCreate(SQLiteDatabaseHolder holder, ConnectionSource connectionSource) {
try {
//...
DataPersisterManager
.registerDataPersisters(MyFieldClassPersister.getSingleton());
} catch (SQLException e) {
// log exception
}
}
And then you can use it in your model like this:
#DatabaseField(persisterClass = MyFieldClassPersister.class, columnName = "column_name")
protected MyFieldClass myFieldClass;
Don't register the persister adapter in the onCreate() method. This method only gets called when your database is first created. You should add this somewhere else, like your constructor or onOpen() method.