How do I create a generalized method around setters? - java

I want to define a DAO over a DynamoDB that has 20+ fields. In Java, I can use Lombok and do something like this to avoid a bunch of boilerplate code.
#Setter
#Getter
#DynamoDBTable("MyTable")
public class MyDAO {
//FIELD_1, FIELD_2, FIELD_3 defined as static final String elsewhere
#DynamoDBAttribute(attribute = FIELD_1)
private final String field1;
#DynamoDBAttribute(attribute = FIELD_2)
private final Long field2;
#DynamoDBAttribute(attribute = FIELD_3)
private final int field3;
...
}
The problem is if I had methods that did something for each field like the following, I would end up duplicating the code over and over again, because the setters in step 2 would be different and the field names in step 3 would be different (i.e. setField1 for the first and setField2 for the second).
public void addField1(String key, String field1Value) {
//Wrap some retry logic and error handling around the following
// 1. get DAO for key
// 2. set FIELD_1 to field1Value in DAO if not set
// 3. put DAO in DynamoDB using attribute name FIELD_1
}
public void addField2(String key, Long field2Value) {
//Wrap some retry logic and error handling around the following
// 1. get DAO for key
// 2. set FIELD_2 to field2Value in DAO if not set
// 3. put DAO in DynamoDB using attribute name FIELD_2
}
Ideally, I would like to have something like the addField method below, with all the retry logic so I don't have to duplicate everything for every field.
private void addField(String fieldName, String key, Object value);
public void addField1(String key, String field1Value) {
addField(FIELD_1, key, (Object) field1Value);
}
I've tried a map between field names and BiConsumers as such
Map<String, BiConsumer<MyDAO, Object>> setterMap =
new HashMap<String, BiConsumer<MyDAO, Object>>(){{
put(FIELD_1, MyDAO::setField1);
put(FIELD_2, MyDAO::setField2);
}};
private void addField(String fieldName, String key, Object value) {
...
// 2. Use setterMap.get(fieldName).accept(value);
...
}
The problem is I get an error saying that I cannot cast BiConsumer<MyDAO, String> to BiConsumer<MyDAO, Object>.
Is it the only way to do it - to create a separate map and method for each type? Or is there a more elegant way to do this?

Well, I don't think it's possible to do it using a Map if you want to preserve type safety. Instead, here's what I would do:
1) I'd create a special class like that:
#AllArgsConstructor
#Getter
final class FieldDefinition<T> {
private final String name;
private final BiConsumer<MyDAO, T> setter;
}
2) Then, I'd create constants in MyDAO (or, even better, in some helper object near MyDAO) like that:
static final FieldDefinition<String> FIELD_1_DEF = new FieldDefinition<>(FIELD_1, MyDAO::setField1);
3) Finally, I'd create the following type-safe addField method:
private <T> void addField(FieldDefinition<T> fieldDefinition, String key, T value) {
// ...
fieldDefinition.getSetter().accept(this, value);
// ...
}
which whould be called like that:
myDao.addField(FIELD_1_DEF, key, value);

Dynamic selection of methods is really not a good fit for functional interfaces. Parameterizing your code around method selection is better done with reflection, rather than with functional interfaces.
The main reason making it difficult to implement your logic using the BiConsumer interface is that you would technically still have to provide static implementations for it, for each field (whether using lambdas, method references, or classes...).
Here's an example reflection-based implementation:
private void addField(String fieldName, String key, Object value) {
MyDAO.class.getDeclaredField(fieldName).set(value, key);
}
So I'd just make setterMap a map of key to field name mapping, and use it like so:
private void addField(String key, Object value) {
String field = setterMap.get(key);
MyDAO.class.getDeclaredField(field).set(value, key);
}

Related

How to map mysql enum type with hypen to JPA entity?

There's a table in mysql sakila.film which has field rating of type enum('G','PG','PG-13','R','NC-17').
I'm trying to map this in JPA entity using type enum and #Enumerated(EnumType.STRING) but not able to, as java enum doesn't allow hyphen.
Can't alter or update table also as it has lots of data and gives error -"Data truncated for column 'rating'"
How to proceed?
This sort of thing is what JPA Attribute Converters are designed to help achieve.
First you'll want to map your Java enum elements to the strings found in the database, and provide a lookup method for converting a string to an element:
public enum Rating {
G("G"), // the string arguments should exactly match what's in the database.
PG("PG"),
PG13("PG-13"),
R("R"),
NC17("NC-17");
private static final Map<String, Rating> LOOKUP = Arrays.stream(values())
.collect(Collectors.toMap(Rating::getRating, Function.identity()));
private final String rating;
Rating(final String rating) {
this.rating = rating;
}
public String getRating() {
return rating;
}
public Rating fromString(final String rating) {
// You may want to include handling for the case where the given string
// doesn't map to anything - implementation is up to you.
return LOOKUP.get(rating);
}
}
Next you're going to want a class that implements javax.persistence.AttributeConverter:
public static class RatingConverter implements AttributeConverter<Rating, String> {
#Override
public String convertToDatabaseColumn(final Rating attribute) {
return attribute.getRating();
}
#Override
public Rating convertToEntityAttribute(final String dbData) {
return Rating.fromString(dbData);
}
}
From here you need to decide whether this converter should always be applied (it sounds like it probably should be), or not.
If you want it to always be used with no further configuration from you, then annotate your converter class with #javax.persistence.Converter(autoApply = true).
If you want to choose when you use the converter, then you will need to add the annotation #javax.persistence.Convert(converter = RatingConverter.class) to the Rating attribute of each JPA entity that needs it.
Personally I usually nest the converters as a static class inside the class that they convert, but you don't have to if you'd rather keep them separate.

Map from property of entry to entry

I often see lists of objects in java holding beans whose objects are picked by inspecting an ID field, i.e.
List<BeanObj> list = …
BeanObj myObj = null;
for(BeanObj b : list)
if(b.getId().equals(whatIAmLookingFor)){
myObj = b;
break;
}
(The second variant of this is storing the objects in Hibernate and retrieve them by SQL.)
Using a Map interface would really be sensible here, but there are difficulties, i.e.
the key field may be changed (in general, or even concurrently)
the key may be non-trivial to reach (think of b.getRoot().getAttribute("id").equals(…)
Have there been approaches to address this in a more efficient way, like implementing a
SpecialMap<String, BeanObj>("id") // use String getId() on BeanObj
or even
SpecialMap<String, BeanObj>("getRoot().getAttribute({0})", "id")
// use String getAttribute("id") on result of getRoot()
with add() instead put() which makes use of the id getter function to build its internal map? Probably this map will require the mapped objects to implement some interface to allow the map being notified of updates on the id field.
Perhaps the map could also take care that changing the ID of an object to an ID of an existing object is either not possible or results in dropping the object that previously had that ID.
You can manage the functionnal aspect of adding element to your map by using guava utilities:
import com.google.common.base.Function;
public class SpecialMap<K, V> extends HashMap<K, V>{
private Function<V, K> function;
public SpecialMap(Function<V, K> function) {
this.function = function;
}
public void add(V value) {
K key = function.apply(value);
this.put(key, value);
}
public static void main(String[] args) {
SpecialMap<String, BeanObj> specialMap = new SpecialMap<String, BeanObj>(new Function<BeanObj, String>() {
#Override
public String apply(BeanObj arg) {
return arg.getRoot().getAttribute("id");
}
});
specialMap.add(new BeanObj());
}
}
In this example, the function will map your bean type to a string key.

Using an enum in Java to represent keys in a ResourceBundle

I have been tinkering with this idea for a few days, and I was wondering if anyone else has thought of doing this. I would like to try and create a ResourceBundle that I can access the values with by using an enum. The benefits of this approach would be that my keys would be well defined, and hopefully, my IDE can pick up on the types and auto-complete the variable names for me. In other words, I'm after a sort of refined ListResourceBundle.
Essentially, this is what I'm after...
I have an enum that consists of various bundles set up like so:
interface Bundle {
String getBundleName();
EnumResourceBundle<??????> getEnumResourceBundle();
}
enum Bundles implements Bundle {
BUNDLE1("com.example.Bundle1", Keys.class);
private final String bundleName;
private final EnumResouceBundle<??????> bundle;
/**
* I understand here I need to do some cast with ResourceBundle.getBundle(bundleName);
* in order to have it back-track through parents properly. I'm fiddling with this
* right now using either what I specified earlier (saving bundleName and then
* retrieving the ResourceBundle as needed), and saving a reference to the
* ResourceBundle.
*/
private <E extends Enum<E> & Key> Bundles(String bundleName, Class<E> clazz) {
this.bundleName = bundleName;
this.bundle = new EnumResourceBundle<??????>(clazz);
}
#Override
public String getBundleName() {
return bundleName;
}
#Override
public EnumResourceBundle<??????> getEnumResourceBundle() {
return bundle;
}
}
interface Key {
String getValue();
}
enum Keys implements Key {
KEY1("This is a key"),
KEY2("This is another key");
private final String value;
private Keys(String value) {
this.value = value;
}
#Override
public String getKey() {
return value;
}
}
class EnumResourceBundle<E extends Enum<E> & Key> extends ResourceBundle {
// Can also store Object in case we need it
private final EnumMap<E, Object> lookup;
public EnumResourceBundle(Class<E> clazz) {
lookup = new EnumMap<>(clazz);
}
public String getString(E key) {
return (String)lookup.get(key);
}
}
So my overall goal would be to have to code look something like this:
public static void main(String[] args) {
Bundles.CLIENT.getEnumResourceBundle().getString(Keys.KEY1);
Bundles.CLIENT.getEnumResourceBundle().getString(Keys.KEY2);
// or Bundles.CLIENT.getString(Keys.KEY1);
}
I'd also like to provide support for formatting replacements (%s, %d, ...).
I realize that it isn't possible to back-track a type from a class, and that wouldn't help me because I've already instantiated Bundles#bundle, so I was wondering if I could somehow declare EnumResourceBundle, where the generic type is an enum which has implemented the Key interface. Any ideas, help, or thoughts would be appreciated. I would really like to see if I can get it working like this before I resort to named constants.
Update:
I had a thought that maybe I could also try changing EnumResourceBundle#getString(E) to take a Key instead, but this would not guarantee that it's a valid Key specified in the enum, or any enum for that matter. Then again, I'm not sure how that method would work when using a parent enum Key within a child EnumResourceBundle, so maybe Key is a better option.
I've done something like this before but I approached it the other way around and it was pretty simple.
I just created an enum translator class that accepts the enum, and then maps the enum name to the value from the property file.
I used a single resource bundle and then the translate just looked something like (from memory):
<T extends enum>String translate(T e) {
return resources.getString(e.getClass().getName()+"."+e.getName());
}
<T extends enum>String format(T e, Object... params) {
return MessageFormat.format(translate(e), params);
}
Now for any enum you can just add a string to the file:
com.example.MyEnum.FOO = This is a foo
com.example.MyEnum.BAR = Bar this!
If you want to ensure that the passed class is the correct enum for this you could either define a shared interface for those enums or you could make this into a class with the T defined on the class type and then generate instances of it for each enum you want to be able to translate. You could then do things like create a translator class for any enum just by doing new EnumFormatter(). Making format() protected would allow you to give a specific enforceable format for each enum type too by implementing that in the EnumFormatter.
Using the class idea even lets you go one step further and when you create the class you can specify both the enum that it is for and the properties file. It can then immediately scan the properties file and ensure that there is a mapping there for every value in the enum - throwing an exception if one is missing. This will help ensure early detection of any missing values in the properties file.

Java data structures, a map that uses an object inside the value as the key

I'm still unsure on how to explain this properly but I will give it my best shot:
I have a set of objects that have values inside that I would like to use as the key for a map.
In order to get the object I'm looking for, I could simply iterate over the set comparing the inner values as I go; this seems inefficient considering one of these values inside the object could be used as a key.
Now the issue is this: were I to change the value inside the object, I also have to update the key to the map, is there not a data structure that would facilitate this task?
In essence; I would like an auto-updating map with keys changing as the value inside changes... I would rather not have to store the map inside the value so, surely there's a more efficient way of thinking than my closed mind?
I hope I've explained myself well enough to get my thinking across.
If I'm getting you right, consider this code:
public class Main{
public static void main(String[] args) {
Test test = new Test(5);
Map<Test, String> map = new HashMap<Test, String>();
map.put(test, "I'm here");
test.value = 10;
System.out.print(map.get(test));
}
}
class Test{
public int value;
Test(int value){
this.value = value;
}
}
As the object you pass into the map as a key is passed by reference, any changes on the object will be reflected in the key set as well. This way you can use entire object as a key, not just the field it contains. You will need to be careful with providing proper equals() and hashCode() methods, though.
I would like an auto-updating map with keys changing as the value inside changes..
Well, you interesting to change parent a.e. storage (Map) on any child change (Value). I don't know if its good design.
If you change the key to Value, map doesn't change
So I would write custom Map like:
public class MyMap extends LinkedHashMap<String, Value> {/**/}
and there I would override put method and implement onValueChange() method by using Interface.
Each child will store instance of Map through interface.
So here we go ....:
RegisterItf
public interface RegisterItf {
public void onValueChange(String newKey, String oldKey, Value newValue);
}
MyMap
public class MyMap extends LinkedHashMap<String, Value> implements RegisterItf{
private static final long serialVersionUID = 1L;
#Override
public Value put(String key, Value value) {
value.setReg(this); // send map instance to each child who registered
return super.put(key, value);
}
#Override
public void onValueChange(String newKey, String oldKey, Value newValue) {
//here you remove old key and add new one
}
}
Value
public class Value {
private String mSomeData;
private String mKey;
private RegisterItf reg;
public void setmKey(String key) {
if(reg != null){
reg.onValueChange(key, mKey, this);
}
this.mKey = key;
}
public void setReg(RegisterItf reg) {
this.reg = reg;
}
}
main
MyMap map = new MyMap();
Value val = new Value();
val.setmKey("aa");
val.setmSomeData("blabla");
map.put(val.getmKey(), val);
...

Java: Access Fields without knowing their names. ("Save reference")

I need to get a Field (or a list of Fields) without knowing it's name.
I.e: for a custom entitymanager i'd like to be able to do Method Calls like this:
cem.getEntities(MyEntity.class, ParamMap) where the ParamMap should be of the Type Map<Field, Object>.
What i can do at the moment is something like this:
Map<Field, Object> params = new HashMap<Field, Object>();
params.put(MyEntity.class.getDeclaredField("someFieldName"), 20);
List<MyEntity> entitysWithSomeFieldNameEquals20 = cem.getEntities(MyEntity.class, params);
Im trying to avoid the usage of querys, because it should work "generic" in the first place, but also be independent from Strings. (They are error-prone). The Entity Manager therefore uses reflection to determine the table and column names, he needs to use.
However, I STILL need to use
MyEntity.class.getDeclaredField("someFieldName")
which will simple move the error-prone string "out" of the entity manager...
What i'm trying to achieve would be something like this:
MyEntity.class.getDeclaredField(MyEntity.class.fields.someFieldName.toString())
So, no matter what the actual field is named, it can be referenced in a save way and refactoring will refactor all the field-access calls, too.
I'm not sure if this is possible. I could go with a (encapsuled) enum for ALL entities, but I hope, that theres a more generic way to achieve this.
Edit:
One good solution seems to be the usage of constants:
public class MyEntity{
private static string SOME_FIELD = "some_field_name_in_database";
#Column(name = SOME_FIELD);
private String someField;
}
...
Map<String, Object> params = new HashMap<String, Object>();
params.put(MyEntity.SOME_FIELD, matchValue);
List<MyEntity> result = eem.getEntities(MyEntity.class, params);
This at least reduces the usage of the string to exactly one location, where it can be maintained and changed without affecting any other file. But im still searching for a solution without constants, so the contants don't need to be synchronized with the available fields :-)
Ok, this is just an idea, which is not easy to implement, but it could work.
Suppose MyEntity looks like this:
public class MyEntity {
private String foo;
private String bar;
public String getFoo() { return this.foo; }
public void setFoo(String foo) { this.foo = foo; }
public String getBar() { return this.bar; }
public void setBar(String bar) { this.bar = bar; }
}
and there is an interface:
public interface Pattern {
public Class<?> getEntityClass();
public Map<Field, Object> getFields();
}
and there is a method, which takes a class and generates a pattern object, which is an instance of the given class:
public class PatternFactory {
public <T> T createPattern(Class<T> klass) {
// magic happens here
}
}
The requirement for the emitted instance would be that it should implement the Pattern interface, such that the method getFields returns only the fields which were explicitly set. GetEntityClass should return the entity class. Then the custom entity manager could be implemented like this:
public class EntityManager {
public <T> Collection<T> getEntities(T pattern) {
if (!(pattern instanceof Pattern))
throw new IllegalArgumentException();
Class<?> klass = ((Pattern) pattern).getEntityClass();
Map<Field, Object> fields = ((Pattern) pattern).getFields();
// fetch objects here
}
}
Then you could use it like this:
PatternFactory pf = // obtain somehow
EntityManager em = // obtain somehow
MyEntity pattern = pf.createPattern(MyEntity.class);
pattern.setFoo("XYZ");
pattern.setBar(null);
Collection<MyEntity> result = em.getEntities(pattern);
In this case pattern.getFields would return a map with two entries.
The difficulty here lies, of course, in the implementation of the createPattern method, where you will have to emit bytecode at run-time. However, this is possible and can be done.

Categories

Resources