I get an object and a map in the method, and I need to migrate all object field values to a map. Which will be later saved in the DB. Values of the map cannot be null.
This is the code:
public static final EMP_LAST_NAME_ATTR = "firstName";
public static final EMP_FIRST_NAME_ATTR = "lastName";
...ect.
and
public void addAttributes(Map<String, String> attributes, Employee employee) {
if (StringUtils.isNotBlank(employee.getFirstName())) {
attributes.put(EMP_FIRST_NAME_ATTR, employee.getFirstName());
}
if (StringUtils.isNotBlank(employee.getLastName())) {
attributes.put(EMP_LAST_NAME_ATTR, employee.getLastName());
}
if (StringUtils.isNotBlank(employee.getEmail())) {
attributes.put(EMP_EMAIL_ATTR, employee.getEmail());
}
...etc many more ifs
}
Unfortunately it has to be a map, as the DB table is created as key/value, and I can't change the the entities.
Any way of shortening this IF nightmare?
One way would be to refactor the if block into its own method:
private void putIfNotBlank(Map<String, String> attributes, String key, String value) {
if (StringUtils.isNotBlank(value)) {
attributes.put(key, values);
}
}
and your method beomes easier to read:
public void addAttributes(Map<String, String> attributes, Employee employee) {
putIfNotBlank(attributes, EMP_FIRST_NAME_ATTR, employee.getFirstName());
putIfNotBlank(attributes, EMP_LAST_NAME_ATTR, employee.getLastName());
putIfNotBlank(attributes, EMP_EMAIL_ATTR, employee.getEmail());
}
private void addAttribute(Map<String, String> attributes, String key, String value) {
if (StringUtils.isNotBlank(value)) {
attributes.put(key, value);
}
}
public void addAttributes(Map<String, String> attributes, Employee employee) {
addAttribute(attributes, EMP_FIRST_NAME_ATTR, employee.getFirstName());
addAttribute(attributes, EMP_LAST_NAME_ATTR, employee.getLastName());
addAttribute(attributes, EMP_EMAIL_ATTR, employee.getEmail());
}
Make a method to do it for you.
public void addAttributes(Map<String,String> attributes, Employee employee)
{
addAttribute(attributes, EMP_FIRST_NAME_ATTR, employee.getFirstName());
addAttribute(attributes, EMP_LAST_NAME_ATTR, employee.getLastName());
.....
}
private void addAttribute(Map<String,String> attributes, String ATTR_NAME, String value)
{
if(StringUtils.isNotBlank(value)) attributes.put(ATTR_NAME, value);
}
Problem solved.
Do some refactoring by creating a utility method that captures the essence of the check:
public static void putIfNotBlank(Map<String, String> map, String key, String value) {
if (StringUtils.isNotBlank(value))
map.put(key, value);
}
Then call it for each attribute:
putIfNotBlank(attributes, EMP_EMAIL_ATTR, employee.getEmail());
Use Jackson Json ObjectMapper library
ObjectMapper mapper = new ObjectMapper();
Map<String,String> map = mapper.convert(mapper.writeValueAsString(<yourObject),new TypeReference<HashMap<String,String>>(){});
You could define a new Map that had this behaviour built in:
public class NonBlankValueMap extends HashMap<String, String> {
#Override
public String put(String key, String value) {
if (StringUtils.isNotBlank(value))
return super.put(key, value);
return null;
}
}
Then simply:
Map<String, String> attributes = new NonBlankValueMap();
And:
attributes.put(EMP_FIRST_NAME_ATTR, employee.getFirstName());
Related
I have an enum like below. Until recently, all variables were single-valued. However, now TYPE4 can have one of three acceptable values. I was hoping to simply modify this enum to accommodate for TYPE4, but thinking perhaps having only one type that is multi-valued means I need to use an object for mapping rather than an enum. I would be grateful for any insights. Thank you.
public enum Record {
TYPE1("TYPE1"),
TYPE2("TYPE2"),
TYPE3("TYPE3"),
TYPE4_MULTI(TYPE_A or TYPE_B or TYPE_C);
private final String value;
public static final Map<Record, String> enumMap = new EnumMap<Record, String>(
Record.class);
static {
for (Record e : Record.values())
enumMap.put(e, e.getValue());
}
Record(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
Operationally, I use this enum in a factory class to determine which of 4 types of subclasses I should instantiate. I do this by have each of the subclasses know its own type like this:
#Override
public String getType() {
return Record.TYPE1.getValue();
}
,and then the factory class pre-builds a set of the subclasses like this:
#Component
public class RecordProcessorFactory {
#Autowired
public RecordProcessorFactory(List<RecordProcessor> processors) {
for (RecordProcessor recordProcessor : processors) {
processorCache.put(recordProcessor.getType(), recordProcessor);
}
}
private static final Map<String, RecordProcessor> processorCache = new HashMap<String, RecordProcessor>();
public RecordProcessor getSyncProcessor(String type) {
RecordProcessor service = processorCache.get(type);
if(service == null) throw new RuntimeException("Unknown service type: " + type);
return service;
}
}
You could use a String array to store multiple values, note that your logic may change with enumMap that way.
public enum Record {
TYPE1("TYPE1"),
TYPE2("TYPE2"),
TYPE3("TYPE3"),
TYPE4_MULTI("TYPE_A", "TYPE_B", "TYPE_C");
private final String[] values;
public static final Map<Record, String[]> enumMap = new EnumMap<Record, String[]>(Record.class);
static {
for (Record e : Record.values())
enumMap.put(e, e.getValues());
}
Record(String... values) {
this.values = values;
}
public String[] getValues() {
return values;
}
}
In case you need to get the Enum from a String value, you could add this static method:
public static Optional<Record> optionalValueOf(final String value) {
for (Record record : values()) {
for (String recordValue : record.values) {
if (null == value && null == recordValue || value.equals(recordValue)) {
return Optional.of(record);
}
}
}
return Optional.empty();
}
I think it's better to encapsulate values in the enum. It should be immutable (array is not immutable data storage).
#lombok.Getter
public enum Record {
TYPE1("TYPE1"),
TYPE2("TYPE2"),
TYPE3("TYPE3"),
TYPE4_MULTI("TYPE_A", "TYPE_B", "TYPE_C");
// immutable list
private final List<String> values;
Record(String... values) {
this.values = Arrays.stream(values)
.collect(Collectors.toList());
}
}
P.S. Map<Record, String> enumMap I think is useless, because you have a Record already and all you need just call record.getValues() instead of Record.enumMaps.get(record). Also, this is breakes OOP encapsulation.
I have a Class like this:
public class MyClass
{
private int id;
private Map<String, String> myMap;
public Map<String, String> getMyMap()
{
return myMap;
}
public void setMyMap(Map<String, String> myMap)
{
this.myMap = myMap;
}
}
I added new setter method(overloading) because i didn't want to do set HashMap directly, and that's what you see now :
public class MyClass
{
private int id;
private Map<String, String> myMap;
public Map<String, String> getMyMap()
{
return myMap;
}
public void setMyMap(Map<String, String> myMap)
{
this.myMap = myMap;
}
public void setMyMap(String key , String value)
{
setMyMap(new HashMap<>(){{put(key, value);}});
}
}
But because i used new HashMap<>(){{put(key, value);}} keyword every time i use this method , it create new Map and last items deleted .
So i have 2 question:
1-correct solution for set items by 2nd setter method
2-how i could use this setter method for multiple put's for this situations:
MyClass.setMyMap(new HashMap<>()
{{
put("title", title);
put("id", id);
}});
Thank you guys for your time .
It depends on what your class does. But in general, I would not expose a setter for a map field.
It makes sense to add a constructor with a map argument, then do something like this:
public class MyClass
{
private final int id;
private final Map<String, String> myMap;
public MyClass(int id, Map<String, String> myMap) {
this.id = id;
this.myMap = myMap;
}
public Map<String, String> getMyMap()
{
return myMap;
}
public void addPairs(Map<String, String> pairs)
{
myMap.putAll(pairs);
}
public void addPair(String key, String value)
{
myMap.put(key, value);
}
}
Of course, you can expose an additional constructor:
public MyClass(int id) {
this.id = id;
this.myMap = new HashMap<>();
}
Try some thing like this:
public void setMyMap(String key , String value) {
if(myMap == null)
myMap = new HashMap<String, String>();
myMap.put(key, value);
}
You've already declared class field myMap and you want to use it in setMyMap method.
Do null check. If the field is null then create a new map. Then use put method to store data in the map.
I'm using Spark 1.6 and trying to solve below problem.
I have a
JavaPairRDD<String, Map<String, List<String>>.
I would like to store this as multiple output files based on the Key of JavaPairRDD being the outer directory and Map's key being the file name.
For example if the JavaPairRDD has the below data
<"A", <{"A1",["a1","b1","c1"]}, {"A2",["a2","b2","c2"]}>>
<"B", <{"B1",["bb1","bb2","bb3"]}>
then the output folders should be as follows
/output/A/A1 (content of A1 should have [a1,b1,c1])
/output/A/A2 (content of A2 should have [a2,b2,c2])
/output/B/B1 (content of B1 should have [bb1,bb2,bb3])
I have the below code but I'm not sure on how I can change the MultipleTextOutputFormat to iterate through the value Map.
public static void main(String a[]) {
JavaPairRDD<String, Map<String, List<String>> pair;
pair.saveAsHadoopFile(directory + "/output", String.class, Map.class,
RDDMultipleTextOutputFormat.class);
}
public static class RDDMultipleTextOutputFormat<A, B> extends MultipleTextOutputFormat<A, B> {
#Override
protected String generateFileNameForKeyValue(A key, B value, String name) {
return key.toString(); // + "/" + name;
}
#Override
protected B generateActualValue(A key, B value) {
//return value;
Map<String, List<String>> map = (HashMap<String, List<String>>)value;
for(Map.Entry<String, List<String>>entry: map.entrySet()) {
generateFileNameForKeyValue((A)(key.toString() + "/" + entry.getKey()), (B)(entry.getValue().toString()), entry.getKey());
}
//return value.saveAsHadoopFile((Map)value., String.class, Map.class,
// RDDMultipleTextOutputFormat.class);
}
#Override
protected A generateActualKey(A key, B value) {
return null;
}
/*#Override
public RecordWriter<A, B> getRecordWriter(FileSystem fs, JobConf job, String name, Progressable prog) throws IOException {
if (name.startsWith("apple")) {
return new TextOutputFormat<A, B>().getRecordWriter(fs, job, name, prog);
} else if (name.startsWith("banana")) {
return new TextOutputFormat<A, B>().getRecordWriter(fs, job, name, prog);
}
return super.getRecordWriter(fs, job, name, prog);
}*/
}
Any help is greatly appreciated.
Thanks
Akhila.
Hello I want to ask about web service, how to show values from database to Map<>?
here is my code
#GET
#Path("/jurusan/{kode_jurusan}")
#Produces(MediaType.APPLICATION_JSON)
public Map getMatkulByjurusan(#PathParam("kode_jurusan") String kode_jurusan){
Map<String, Object> rs = new HashMap<String, Object>();
rs.put("Pesan", "Berhasil");
System.out.println("Jurusan "+kode_jurusan);
try {
createConnection();
MyMap matkul =(MyMap) jt.queryObject("select matkul from tb_matkul where kode_jurusan = ?", new Object[] {kode_jurusan}, new MyMap());
closeConnection();
if(matkul != null){
rs.put("result", matkul);
}
} catch (Exception e) {
rs.put("Pesan", "Gagal karena : " +e.getMessage());
}
return rs;
}
}
but when I try to acess http://localhost:9090/Service/matkul/jurusan/40 I get the following message:
{"Pesan":"Gagal karena : Incorrect result size: expected 1, actual 14"}
this MyMap class
public class MyMap implements Serializable, RowMapper{
private static final long serialVersionUID = -8840406844877458198L;
public HashMap<String, Object> map = new HashMap<String, Object>();
public HashMap<String, Object> getMap() {
return map;
}
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
MyMap dto=new MyMap();
int rowCount = rs.getMetaData().getColumnCount();
for (int i = 1; i <= rowCount; i++) {
dto.map.put(rs.getMetaData().getColumnLabel(i), rs.getObject(i));
}
return dto;
}
public void put(String name, Object o){
map.put(name, o);
}
public Object get(String name){
return map.get(name);
}
public String getString(String name){
return (String)map.get(name);
}
public Integer getInt(String name){
return (Integer)map.get(name);
}
public Date getDate(String name){
return (Date)map.get(name);
}
public BigDecimal getBigDecimal(String name){
return (BigDecimal)map.get(name);
}
}
Use queryForList method instead queryObject.
you can find an example to map multiple rows to list using jdbcTemplate from here .
Looks like the problem is on the database query, as the exception says, the query is expecting only 1 row as result and it produces 14.
Moreover, depending on which framework are you using you should probably provide a way to serialize the MyMap class
I have class Rowdata
public class FileData {
Map<Long, List<String>> cacheData = new LinkedHashMap<Long, List<String>>();
List<String> header = new ArrayList<String>();
public Map<Long, List<String>> getCacheData() {
return Collections.unmodifiableMap(cacheData);
}
public List<String> getHeaderMapping() throws Exception {
return header;
}
public void putData(long key, List<String> row) {
cacheData.put(key, row);
}
public int size() {
return cacheData.size();
}
public void setHeader(List<String> header) {
this.header = header;
}
}
I would like persist this object. What is the best way to persist? Shall I proceed with RDBMS? then what is the table schema?
Or Shall I proceed with NoSQL? then We already have CouchDB in our project . Please advice on this.