日常項目中經常有這樣的需求,即需要幾個緊密相關的内容存儲起來,例如,xxx省xxx市xxx區等等。
例如下圖:
這些緊密相關的内容可能會經常增加或者減少某項内容,在資料庫應用中,當然可以設計幾個字段來存儲或者設計一個專門的key-value表來存儲這些可變内容,但是對于這類不會直接用條件進行檢索的緊密關聯的内容來講,儲存在一個字段traffic_description中似乎更為妥帖一點。既然要存儲在一個字段中那麼就涉及到如何分割群組裝的問題。
一個比較直接的做法就是使用分号對每項值進行分割存儲,這種方式屬于一種平面的結構,還有一個更好的辦法就是将這些字段組裝成一個json 字元串,格式如{\'key1\':value1,\'key2\':[\'value2\',\'value21\']},這樣就可以直接利用現成的對象存儲方式來儲存,目前就有很多第三方包對json提供了支援,如:java中提供了json-lib.jar,其他語言支援包見
www.json.org
下面提供一種簡單的實作:
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.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import com.alibaba.common.logging.Logger;
import com.alibaba.common.logging.LoggerFactory;
/**
*
* <p>
* json 字元串生成和解析base類
* </p>
*
* @author <a href="mailto:[email protected]" mce_href="mailto:[email protected]">清虛</a>
* @since 2.0 2009-10-19上午10:42:57
*
*/
public class JsonDO {
private static final Logger log = LoggerFactory.getLogger("JsonDO");
/**
* json 屬性标注 ,表示該屬性需要放入json對象
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JsonField {
}
private String value;// json字元串,一般存儲在資料庫中
/**
* 調用該方法會設定value屬性,該方法一般在儲存資料庫時調用
*
* @return the value
*/
public String getValue() {
encode();
return value;
}
/**
* 調用該方法會解析value字元串,并設定相應的java 屬性,該方法一般在從資料庫中load時調用
*
* @param value
* the value to set
*/
public void setValue(String value) {
this.value = value;
decode();
}
/**
*
*
* <p>
* 将value json中的屬性值設定到對應的java DO屬性中去,java 屬性必須有JsonField 标注才會被設定
* </p>
*
* @author <a href="mailto:[email protected]" mce_href="mailto:[email protected]">清虛</a>
* @since 2.0 2009-10-19下午02:39:56
*
*/
@SuppressWarnings("unchecked")
protected void decode() {
if (this.value == null || this.value.trim().equals("")) {
return;
}
Map<String, Class> classMap = new HashMap<String, Class>();
Field[] fs = getClass().getDeclaredFields();
for (Field f : fs) {
if (f.isAnnotationPresent(JsonField.class)) {
classMap.put(f.getName(), f.getClass());
}
}
if (classMap.isEmpty()) {
return;
}
JSONObject json = JSONObject.fromObject(this.value);
JsonDO o = (JsonDO) JSONObject.toBean(json, getClass(), classMap);
Set<String> set = classMap.keySet();
Iterator<String> it = set.iterator();
while (it.hasNext()) {
String name = it.next();
try {
Object value = PropertyUtils.getSimpleProperty(o, name);
BeanUtils.copyProperty(this, name, value);
} catch (Exception e) {
log.error("copy prorperty error src=" + this.value, e);
}
}
}
/**
*
*
* <p>
* 将JsonField 标注了的基本屬性組裝成json字元串儲存在value中
* </p>
*
* @author <a href="mailto:[email protected]" mce_href="mailto:[email protected]">清虛</a>
* @since 2.0 2009-10-19下午02:41:27
*
*/
@SuppressWarnings("unchecked")
protected void encode() {
JSONObject json = new JSONObject();
Field[] fs = getClass().getDeclaredFields();
for (Field f : fs) {
if (f.isAnnotationPresent(JsonField.class)) {
String name = f.getName();
Object value = null;
try {
value=PropertyUtils.getSimpleProperty(this, name);
//value = f.get(this);
} catch (Exception e) {
log.error("get field value failed! fieldName=" + name, e);
}
if (value == null) {
continue;
}
Class type = f.getType();
if (Collection.class.isAssignableFrom(type)) {
Collection it = (Collection) value;
for (Object v : it) {
json.accumulate(name, v);
}
} else if (type.isArray()) {
json.put(name, JSONArray.fromObject(value));
} else {
json.put(name, value);
}
}
}
this.value=json.toString();
//setValue(json.toString());
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
使用方式如下:
public class TrafficDO extends JsonDO {
/**
*序列号
*/
private static final long serialVersionUID = -5673233152393642499L;
@JsonField
private String invoiceTitle; //發票擡頭
@JsonField
private String fullName; //配送人姓名
@JsonField
private String mobileTel; //配送手機号碼
@JsonField
private String province; //省
@JsonField
private String city; //市
@JsonField
private String section; //區
@JsonField
private String address; //詳細位址
@JsonField
private String post; //郵編
private int shipmentType; //配送方式
public static final int BY_MAIL_TYPE = 0; //挂号信郵寄方式
public static final int BY_NOTNEED = -1; //不需要行程單
/**
* @return the shipmentType
*/
public int getShipmentType() {
return shipmentType;
}
/**
* @param shipmentType the shipmentType to set
*/
public void setShipmentType(int shipmentType) {
this.shipmentType = shipmentType;
}
/**
* @return the fullName
*/
public String getFullName() {
return fullName;
}
/**
* @param fullName the fullName to set
*/
public void setFullName(String fullName) {
this.fullName = fullName;
}
/**
* @return the mobile
*/
public String getMobileTel() {
return mobileTel;
}
/**
* @param mobile the mobile to set
*/
public void setMobileTel(String mobile) {
this.mobileTel = mobile;
}
/**
* @return the province
*/
public String getProvince() {
return province;
}
/**
* @param province the province to set
*/
public void setProvince(String province) {
this.province = province;
}
/**
* @return the city
*/
public String getCity() {
return city;
}
/**
* @param city the city to set
*/
public void setCity(String city) {
this.city = city;
}
/**
* @return the section
*/
public String getSection() {
return section;
}
/**
* @param section the section to set
*/
public void setSection(String section) {
this.section = section;
}
/**
* @return the address
*/
public String getAddress() {
return address;
}
/**
* @param address the address to set
*/
public void setAddress(String address) {
this.address = address;
}
/**
* @return the post
*/
public String getPost() {
return post;
}
/**
* @param post the post to set
*/
public void setPost(String post) {
this.post = post;
}
/**
* 郵寄
*
* @return
*/
public boolean isMail() {
return this.shipmentType == BY_MAIL_TYPE;
}
/**
* 不需要
*
* @author qingxu
* @since 2007-10-25下午04:48:12
*
* @return
*/
public boolean isNotNeed() {
return this.shipmentType == BY_NOTNEED;
}
public String getInvoiceTitle() {
return invoiceTitle;
}
public void setInvoiceTitle(String invoiceTitle) {
this.invoiceTitle = invoiceTitle;
}
}
@JsonField标注支援 基本類型和數組、collection類型域。
在從DB中load出traffic_description的值後
通過setValue()方法設定進去,那麼所有的json域都會被填充好。同樣的,如果填充了相應的json域,
在存儲在資料庫中時,可以調用getValue()得到字元串持久化到資料庫中。
通過getValue()得到的字元串形式如下:
{"section":"馬龍縣","fullName":"叮當","address":"vvvvvv","province":"雲南省","invoiceTitle":"vvvvvv","mobileTel":"136**622627","post":"310014","city":"曲靖市"}
這種處理方式的好處是,可以友善的定義你自己要存儲的内容,你所有做的僅僅是在新增一個field,然後在上面打上@JsonField标注即可。
而它的缺點也是明顯的,1、這種方式明顯增加了存儲内容的長度 2、由于存儲的key是屬性的名稱,如果名稱不比對也會找不到對應的值。3、如果要對其中的内容進行條件檢索,隻有進行文本比對,如果有這種需求,這種方式不推薦。
