一、業務需求
假設某學校課程系統,不同專業課程不同 (可以動態增刪),但是需要根據專業不同顯示該專業學生的各科課程的成績,如下:
專業 | 姓名 | 高等數學 | 數據結構 |
---|---|---|---|
計算機 | 張三 | 90 | 85 |
計算機 | 李四 | 78 | 87 |
專業 | 姓名 | 高等數學 |
---|---|---|
數學 | 王五 | 86 |
數學 | 趙六 | 95 |
二、設計思路
開始的思路是根據配置的課程動態生成文檔字段,使用非映射方式直接操作 MongoCollection, 有以下問題:
- 存取數據日期序列化問題 (亦可能是本人沒有找到正確的處理方式)
- 返回結果集不能轉換成實體對象,不方便做二次處理
所以最終使用內嵌數組的方式
三、代碼示例
3.1 實體類
public class Student {
private String name;
private String major;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@JsonIgnore
private List<Course> courseList;
/* getter setter*/
}
public class Course {
private String name;
private Float score;
/* getter setter*/
}
3.2 添加測試專業及課程
public class MajorConfig {
private static MajorConfig computer;
private static MajorConfig math;
static {
computer = new MajorConfig();
computer.setName("計算機");
List<String> courseList = new ArrayList<>();
courseList.add("高等數學");
courseList.add("數據結構");
computer.setCourseList(courseList);
math = new MajorConfig();
math.setName("數學");
courseList = new ArrayList<>();
courseList.add("高等數學");
math.setCourseList(courseList);
}
private String name;
private List<String> courseList;
/* getter setter*/
}
3.3 添加測試數據到 MongoDB
初始化數據 (createTime 在 MongoDB 存儲為 ISODate):
[
{
"name": "張三",
"major": "計算機",
"createTime": "2018-01-20 08:00:00",
"courseList": [
{
"name": "高等數學",
"score": 20.283026
},
{
"name": "數據結構",
"score": 30.612194
}
]
},
{
"name": "王五",
"major": "數學",
"createTime": "2018-01-20 08:00:00",
"courseList": [
{
"name": "高等數學",
"score": 91.78229
}
]
},
{
"name": "李四",
"major": "計算機",
"createTime": "2019-10-01 07:10:50",
"courseList": [
{
"name": "高等數學",
"score": 60.488556
},
{
"name": "數據結構",
"score": 80.66098
}
]
},
{
"name": "趙六",
"major": "數學",
"createTime": "2019-10-01 07:10:50",
"courseList": [
{
"name": "高等數學",
"score": 29.595625
}
]
}
]
3.4 根據專業獲取學生課程分數列表
首先查詢到對應的數據,然后根據配置的課程動態添加字段:
public Object list(String major){
MajorConfig majorConfig = getMajorConfig(major);
Query query = new Query(Criteria.where("major").is(major));
List<Student> studentList = mongoTemplate.find(query, Student.class);
List<Object> result = new ArrayList<>();
for(Student student:studentList){
Map<String,Object> properties = Maps.newHashMap();
for(String name : majorConfig.getCourseList()){
properties.put(name,null);
for(Course course : student.getCourseList()){
if(name.equals(course.getName())){
properties.put(name,course.getScore());
break;
}
}
}
result.add(ReflectUtil.getTarget(student,properties));
}
return result;
}
各專業學生各科分數數據:
[
{
"name": "王五",
"major": "數學",
"createTime": "2018-01-20 08:00:00",
"高等數學": 91.78229
},
{
"name": "趙六",
"major": "數學",
"createTime": "2019-10-01 07:10:50",
"高等數學": 29.595625
}
]
[
{
"name": "張三",
"major": "計算機",
"createTime": "2018-01-20 08:00:00",
"高等數學": 20.283026,
"數據結構": 30.612194
},
{
"name": "李四",
"major": "計算機",
"createTime": "2019-10-01 07:10:50",
"高等數學": 60.488556,
"數據結構": 80.66098
}
]
ReflectUtil 代碼:
public class ReflectUtil {
public static Object getTarget(Object dest, Map<String, Object> addProperties) {
PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
PropertyDescriptor[] descriptors = propertyUtilsBean.getPropertyDescriptors(dest);
Map<String, Class> propertyMap = Maps.newHashMap();
for (PropertyDescriptor d : descriptors) {
if (!"class".equalsIgnoreCase(d.getName())) {
propertyMap.put(d.getName(), d.getPropertyType());
}
}
// add extra properties
addProperties.forEach((k, v) -> {
if(v != null){
propertyMap.put(k, v.getClass());
}else{
propertyMap.put(k, Object.class);
}
});
// new dynamic bean
DynamicBean dynamicBean = new DynamicBean(dest.getClass(), propertyMap);
// add old value
propertyMap.forEach((k, v) -> {
try {
// filter extra properties
if (!addProperties.containsKey(k)) {
dynamicBean.setValue(k, propertyUtilsBean.getNestedProperty(dest, k));
}
} catch (Exception e) {
e.printStackTrace();
}
});
// add extra value
addProperties.forEach((k, v) -> {
try {
dynamicBean.setValue(k, v);
} catch (Exception e) {
e.printStackTrace();
}
});
Object target = dynamicBean.getTarget();
return target;
}
}
DynamicBean 代碼:
public class DynamicBean {
private Object target;
private BeanMap beanMap;
public DynamicBean(Class superclass, Map<String, Class> propertyMap) {
this.target = generateBean(superclass, propertyMap);
this.beanMap = BeanMap.create(this.target);
}
public void setValue(String property, Object value) {
beanMap.put(property, value);
}
public Object getValue(String property) {
return beanMap.get(property);
}
public Object getTarget() {
return this.target;
}
private Object generateBean(Class superclass, Map<String, Class> propertyMap) {
BeanGenerator generator =new BeanGenerator();
if(null != superclass) {
generator.setSuperclass(superclass);
}
BeanGenerator.addProperties(generator, propertyMap);
return generator.create();
}
}
3.5 添加課程並修改數據庫
public Object add(String major, String name, Boolean updateDatabase){
MajorConfig majorConfig = getMajorConfig(major);
List<String> courseList = majorConfig.getCourseList();
if(!courseList.contains(name))
{
courseList.add(name);
majorConfig.setCourseList(courseList);
}
if(updateDatabase){
Random random = new Random();
Course course = new Course();
course.setName(name);
course.setScore(random.nextFloat() * 100);
Update update = new Update();
update.addToSet("courseList", course);
Query query = Query.query(Criteria.where("major").is(majorConfig.getName()));
mongoTemplate.updateMulti(query, update, Student.class);
}
return majorConfig;
}
為數學專業添加計算機基礎:
[
{
"name": "王五",
"major": "數學",
"createTime": "2018-01-20 08:00:00",
"計算機基礎": 9.042096,
"高等數學": 91.78229
},
{
"name": "趙六",
"major": "數學",
"createTime": "2019-10-01 07:10:50",
"計算機基礎": 9.042096,
"高等數學": 29.595625
}
]
3.6 刪除課程並修改數據庫
public Object del(String major, String name, Boolean updateDatabase){
MajorConfig majorConfig = getMajorConfig(major);
List<String> courseList = majorConfig.getCourseList();
if(courseList.contains(name)){
courseList.remove(name);
majorConfig.setCourseList(courseList);
}
if(updateDatabase){
Update update = new Update();
update.pull("courseList", new BasicDBObject("name", name));
Query query = Query.query(Criteria.where("major").is(majorConfig.getName()));
mongoTemplate.updateMulti(query,update,Student.class);
}
return majorConfig;
}
把高等數學從計算機專業刪除:
[
{
"name": "張三",
"major": "計算機",
"createTime": "2018-01-20 08:00:00",
"數據結構": 30.612194
},
{
"name": "李四",
"major": "計算機",
"createTime": "2019-10-01 07:10:50",
"數據結構": 80.66098
}
]
3.7 修改某學生的某課程分數
public Object update(String name, String courseName, Float score){
Query query = Query.query(Criteria.where("name").is(name).and("courseList.name").is(courseName));
Update update = new Update();
update.set("courseList.$.score", score);
mongoTemplate.updateFirst(query, update, Student.class);
return null;
}
完整代碼:GitHub