数据库中控制某一字段字符长度的应用


在工作中遇到了这么一个场景:

在数据库中需要对表的某个字段进行字符追加,并且字符追加在字段前端,对字符的长度有限制,

例如:保持字段的长度在100以内,每次追加固定字符,在字段的前面,当字段超过100时,截掉超出的部分。

 

根据实际的情况,我们提出了3种解决方案:

1 实现一种queue,保持其中的容量固定,并最后序列化进数据库,当需要插入字符,从数据库中取出,反序列化,向queue中插入数据,

并且当数据超过容量时自动截掉最先插入的数据,最后再将其序列化进入数据库;

2 编写原生的mysql语句,对字符串进行操作;

3 由于项目中采用JPA,因此找到一种jpql语句,与2实现功效相当。

 

经过思考,比较了三种方案的优缺点:

方案一:容易实现,java实现,通用性也不错,但是当插入字符串时,对数据库操作存在事务性,当并发操作时,会产生问题。

方案二:采用一条sql语句实现,简单,没有事务性存在,不用考虑并发的问题,但是如果数据库更换,需要重写sql语句。

方案三:采用标准的jpql语句实现,是一种标准,对于数据库更换不存在修改问题,没有事务性,不需要考虑并发问题。

综上所述,采用第三种方案是最优的。

 

在实验过程中,实现了方案一和方案三:

方案一:

用于存放字符串的queue

 

 1 import java.io.Serializable;
 2 import java.util.ArrayList;
 3 import java.util.LinkedList;
 4 import java.util.List;
 5
 6 public class StrsQueue implements Serializable {
 7
 8     /**
 9      *
10      */
11     private static final long serialVersionUID = -204264456311340382L;
12
13     private int totalSize;// 记录queue的容量
14     private LinkedList<String> queue;
15
16     public StrsQueue (int totalSize) {
17         queue = new LinkedList<String>();
18         this.totalSize=totalSize;
19     }
20
21    
22
23     public void addStr(String str) {
24         queue.add(0, str);
25         if (queue.size() > totalSize) {
26             queue.removeLast();//超过容量,删掉最先插入的数据
27         }
28     }
29
30     public List<String> obtainStrs( int pageNumber,int pageSize) {
31         int offset=(pageNumber-1)*pageSize;
32         if (offset >= totalSize) {
33             return null;
34         }
35         ArrayList<String> list = new ArrayList<String>();
36         int length=Math.min(queue.size(), totalSize);
37 //        int length = (queue.size() < totalSize) ? queue.size() : totalSize;
38         if ((offset + pageNumber) > length || pageSize == 0) {
39             for (int i = offset; i < length; i++) {
40                 String feedId = (String) queue.get(i);
41                 list.add(feedId);
42             }
43         } else {
44             for (int i = offset; i < pageSize; i++) {
45                 list.add((String) queue.get(i));
46             }
47         }
48         return list;
49     }
50
51 }

使用StrQueue的Entity,

  2
  3 import java.io.ByteArrayInputStream;
  4 import java.io.ByteArrayOutputStream;
  5 import java.io.IOException;
  6 import java.io.ObjectInputStream;
  7 import java.io.ObjectOutputStream;
  8
  9 import javax.persistence.Column;
 10 import javax.persistence.Entity;
 11 import javax.persistence.Id;
 12 import javax.persistence.Table;
 13 import javax.persistence.Transient;
 14
 15
 16 /**
 17  * UserFeedInbox entity. @author MyEclipse Persistence Tools
 18  */
 19 @Entity
 20 @Table(name = "table_name", catalog = "database")
 21 public class UserFeedInboxEntry implements java.io.Serializable {
 22     // Fields
 23
 24    
 25     private static final long serialVersionUID = 840144016005162535L;
 26     private String id;
 27     private String userId;
 28     private String userType;
 29     private byte[] strs;
 30
 31     private StrsQueue strsQueue;
 32    
 33     // Constructors
 34
 35     /** default constructor */
 36     public UserFeedInboxEntry() {
 37     }
 38
 39     /** minimal constructor */
 40     public UserFeedInboxEntry(String id) {
 41         this.id = id;
 42     }
 43
 44     /** full constructor */
 45     public UserFeedInboxEntry(String id, String userId, String userType,
 46             byte[] strs) {
 47         this.id = id;
 48         this.userId = userId;
 49         this.userType = userType;
 50         this.strs = strs;
 51     }
 52
 53     // Property accessors
 54     @Id
 55     @Column(name = "id", unique = true, nullable = false, length = 36)
 56     public String getId() {
 57         return this.id;
 58     }
 59
 60     public void setId(String id) {
 61         this.id = id;
 62     }
 63
 64     @Column(name = "user_id", length = 36)
 65     public String getUserId() {
 66         return this.userId;
 67     }
 68
 69     public void setUserId(String userId) {
 70         this.userId = userId;
 71     }
 72
 73     @Column(name = "user_type", length = 50)
 74     public String getUserType() {
 75         return this.userType;
 76     }
 77
 78     public void setUserType(String userType) {
 79         this.userType = userType;
 80     }
 81
 82     @Column(name = "strs", columnDefinition = "blob")
 83     public byte[] getStrs() {
 84         return this.strs;
 85     }
 86
 87     public void setStrs(byte[] strs) {
 88         this.strs = strs;
 89
 90         ByteArrayInputStream bais;
 91         ObjectInputStream in;
 92         try {
 93             bais = new ByteArrayInputStream(strs);
 94             in = new ObjectInputStream(bais);
 95             strsQueue = (StrsQueue) in.readObject();
 96             in.close();
 97         } catch (IOException ex) {
 98             ex.printStackTrace();
 99         } catch (ClassNotFoundException ex) {
100             ex.printStackTrace();
101         }
102     }
103
104     @Transient
105     public StrsQueue getFeedsIdsQueue() {
106         return strsQueue;
107     }
108
109     public void setStrsQueue(StrsQueue strsQueue) {
110         this.strsQueue = strsQueue;
111         ByteArrayOutputStream baos;
112         ObjectOutputStream out;
113         baos = new ByteArrayOutputStream();
114         try {
115             out = new ObjectOutputStream(baos);
116             out.writeObject(strsQueue);
117             out.close();
118         } catch (IOException e) {
119             e.printStackTrace();
120         }
121         this.strs = baos.toByteArray();
122     }
123
124 }


注:开始时设计的Queue中,采用的底层实现是ArrayList,在queue中记录一个position变量,插入字符串时直接插入到arraylist的尾部,并将position指到当前位置,当queue的字符串的数量达到容量时,position指向arraylist的0位置,从头开始set值,这样能够保证容量不变,并且不需要对list进行删除操作,当初做这种设计是考虑当容量大时,用linkedlist虽然插入和删除动作对链表结构性能影响不大,但是如果从链表中取值的话需要遍历,如果取的值很多,并且取值的位置不固定时,linkedlist的取值效率没有arraylist直接根据下标取值的效率高,因此最初将底层设计为arraylist,但是最后跟头讨论了一下,本项目的queue的容量没有那么大,linkedlist取值的效率问题不需要考虑,因此改为采用linkedlist实现,代码比较清晰易懂。

 

方案三:本项目采用spring data for jpa 实现,

 2
 3 import org.springframework.data.jpa.repository.Modifying;
 4 import org.springframework.data.jpa.repository.Query;
 5 import org.springframework.data.repository.CrudRepository;
 6 import org.springframework.data.repository.query.Param;
 7
 9
10 public interface MyEntryJpaRepo extends CrudRepository<MyEntry, String> {


    //根据offset和length获取字符串
28     @Query("SELECT SUBSTRING(m.strs,:offset, :length) FROM MyEntry m WHERE m.userId = :userId AND m.userType = :userType")
29     public String obtainStrs(@Param("userId")String userId, @Param("userType")String userType,@Param("offset")int offset,@Param("length")int length);
30    
31     @Modifying
32     @Query("UPDATE MyEntry m  SET m.strs = CONCAT(CONCAT(:str ,','),SUBSTRING(m.strs,1,:strsLength)) WHERE m.userId = :userId AND m.userType = :userType")
33     public void addStrToInbox(@Param("userId")String userId, @Param("userType")String userType,@Param("str")String str,@Param("idsLength")int idsLength);
34
35 }


最后做了单元测试,就不一一介绍了,但是测试时发现个问题,当spring data jpa中的offset为负值时,数据从字段的后面开始数并截取相应长度的字符串,当offset为0时,返回空串“”。

 

posted @ 2012-09-06 19:48  yanzhenxing  阅读(629)  评论(0编辑  收藏  举报