一、领域模型先行的思想
上来第一步不是根据产品经理给的需求建数据库、表。
而是先进行领域模型设计,再建库、表。
比如,先想好,密码和用户信息应该分开存放,然后再去建表。
也就是说,先思考设计Model,再去动手建表。
二、Mybatis进行ORM
两个连接数据库的地方
- 在Mybatis配置文件中,为映射数据库表做准备
- 在SpringBoot项目配置文件中,为CURD操作做准备
Mybatis映射完成后,要检查一遍,DO中属性的数据类型是否和数据库中类型一致
还需要手动在mapper.xml文件的insert方法和insertSelective方法后添加id主键自增的配置
三、服务实现
根据Model设计Service接口,再进行实现
-
首先类级别加@Service;createItem方法加@Transactional
-
方法体内首先要进行入参校验;
入参校验第一步是在Model中加上注解;
然后在ServiceImpl中@AutoWired自动引入ValidatorImpl对象;
接着就是调用validator对象的validate方法对Model进行校验;
如果异常,则抛出
-
然后就是model转dataobject
首先判断Model是否为空,如果为空,直接返回null,
然后new一个ItemDO对象,
使用BeanUtils.copyProperties(source, target);进行copy
注意,Model中price是BigDecimal类型,而DO中price是Double类型
这样会导致copy忽略price
所以,copy之后,还需要手动转换price的类型,
再手动set到dataobject中
数据库中没有BigDecimal,所以只能使用Double
如果Service也用Double的话 ,有肯能在返回给Controller,Controller再JSON序列化返回给前端时,导致精度丢失,造成死锁。
如果是BigDecimal的话,返回的时候精度是确定的,不会丢失。
还有一个需要转化的就是ItemStockDO
前两步一样,判空,new
第三步不能使用copy了,因为那样会让id错误
只能使用set方法
-
将dataobject写入数据库
因为要操作数据库,所以要
@AutoWired一个itemDOMapper对象
@AutoWired一个ItemStockDOMapper对象
用它俩来执行数据库的insertSelective方法
插入成功后,再从数据库里读出来,返回给调用的方法
这样,方法在调用的时候,如果发现返回值为null
这说明插入数据库的过程有问题
-
getItemById的实现
这其实属于第3步的返回值,Service从Controller接收Model
然后插入数据库后,再从数据库中取出来,返回给Controller
这样,就可以真的确定,Model入库了。
实现原理就是根据传入参数id,先去数据库中查出来ItemDO
再根据ItemDO在数据库中的id,即item_stock的外键,来查出来ItemStockDO
(其实传入参数id就是itemDO在数据库中的id,不知道老师为什么不那样用)
(那样用确实存在风险,但是我不明白风险什么时候能发生,先记录于此)
最后再将两个DO整合为Model,返回
这里也要注意price类型转换的问题
四、Controller实现
-
创建一个ItemVO
这里的ItemVO和ItemModel完全一致,但是老师有强调到,有些情况,VO层会扩增,所以分层是必须的。
这里还要强调,VO必须有getter、setter
否则会创建失败
原因正在思考中…
-
创建一个ItemController,依然继承与BaseController
然后在类级别上加上@Controller声明、RequestMapping映射、@CrossOrigin跨域
老师在这里强调,我们所做的分层,就是尽可能让Controller层简单,让service层复制,把业务逻辑都尽可能的聚合在Service中
-
实现Controller层的createItem方法
先加上@RequestMapping映射,和@ResponseBody
声明CommonReturnType类型的方法createItem以及参数映射
老师在这里接收的参数是具体的类型
我还是主张所以参数先用String类型接收
接收到后再转为对应的类型,这样前端传递的容易一些方法体就是 封装参数成Model,调用Service,返回创建的商品Model
把返回的Model转为VO,通过CommonReturnType.create(itemVO);
返回给前端
-
convertVOFromModel的实现
接收service层返回的Model
判空
new VO对象
BeanUtils复制
返回VO
-
Controller层getItemList方法实现
先加上@RequestMapping映射,和@ResponseBody
声明CommonReturnType类型的方法getItemList以及参数映射
老师在这里接收的参数是具体的类型
我还是主张所以参数先用String类型接收
接收到后再转为对应的类型,这样前端传递的容易一些方法体就是接收id,ItemService从数据库里查出Model
再将Model转为VO
CommonReturnType进行返回
五、CreateItem前端实现
注意URL请求地址一定要正确即可
六、listItem在DAO层实现
- ItemDOMapper.xml新增查询
<select id="listItem" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from item order by sales desc;
</select>
上面的语句相当于 SELECT * FROM item ORDER BY sales DESC;
- ItemDOMapper.java新增映射方法
List<ItemDO> listItem();
七、listItem在Service层实现
- ItemService.java声明接口方法
- ItemServiceImpl.java实现接口方法
@Override
public List<ItemModel> listItem() {
List<ItemDO> itemDOList = itemDOMapper.listItem();
// Java8 的 stream API map、collect、Collection.toList()
List<ItemModel> itemModelList = itemDOList.stream().map(
itemDO -> {
ItemStockDO itemStockDO = itemStockDOMapper.selectByItemId(itemDO.getId());
ItemModel itemModel = this.convertModelFromDataObject(itemDO, itemStockDO);
return itemModel;
}
).collect(Collectors.toList());
return itemModelList;
}
八、listItem在Controller层实现
@RequestMapping(value = "list", method = {RequestMethod.GET})
@ResponseBody
public CommonReturnType listItem(){
List<ItemModel> itemModelList = itemService.listItem();
// 使用Java8 stream API将list内的itemModel转为itemVO
List<ItemVO> itemVOList = itemModelList.stream().map(
itemModel -> {
ItemVO itemVO = this.convertVOFromModel(itemModel);
return itemVO;
}
).collect(Collectors.toList());
return CommonReturnType.create(itemVOList);
}
九、listItem Android端实现
布局
ConstraintLayout 内嵌 RecyclerView, 实现可回收滚动列表视图
CardView 内嵌 LinearLayout,inflate Adapter,进而recyclerView.setAdapter(itemVOAdapter);实现卡片式布局。
逻辑
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_item);
// 查找控件
RecyclerView recyclerView = findViewById(R.id.rv_item_list);
// 设置布局管理器
GridLayoutManager layoutManager = new GridLayoutManager(this, 2);
recyclerView.setLayoutManager(layoutManager);
// 设置数据源
itemVOList = new ArrayList<>();
itemVOAdapter = new ItemVOAdapter(this, itemVOList);
// 设置适配器
recyclerView.setAdapter(itemVOAdapter);
// 加载网络数据
loadUrlData(URL);
// 下拉刷新
swipeRefresh = findViewById(R.id.sr_item_list);
swipeRefresh.setColorSchemeResources(R.color.colorPrimary);
swipeRefresh.setOnRefreshListener(
new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
refreshItemList();
itemVOAdapter.notifyDataSetChanged();
swipeRefresh.setRefreshing(false);
}
}
);
}
下拉刷新的实现,OnRefreshListener()内部方法的实现
// 下拉刷新的实现
private void refreshItemList() {
new Thread(
new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
loadUrlData(URL);
itemVOAdapter.notifyDataSetChanged();
swipeRefresh.setRefreshing(false);
Log.d(TAG, "刷新完成!");
}
});
}
}).start();
}
加载网络数据的实现,重点是需要用到AsyncTask<Void, Void, String>(){ … }
// 加载网络数据的实现
@SuppressLint("StaticFieldLeak")
private void loadUrlData(final String url) {
new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... params) {
// 执行网络请求
String responseBodyStr = getResponseBodyStrByokhttp(url);
return responseBodyStr;
}
@Override
protected void onPostExecute(String responseBodyStr) {
if ( !TextUtils.isEmpty(responseBodyStr) ) {
// 使用前先清除数据
itemVOList.clear();
// 解析数据
// 这里不能使用赋值语法,必须使用AddAll,否则无法显示数据
itemVOList.addAll(parseJSONData(responseBodyStr));
// 打乱数据
Collections.shuffle(itemVOList);
// 提示adapter更新数据
itemVOAdapter.notifyDataSetChanged();
}
}
}.execute();
}
Gson解析JSON的实现,重点是强大的Gson可以快速将JsonArray转为List
// okhttp获取ResponseBodyStr
private String getResponseBodyStrByokhttp(String path) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().get().url(path).build();
try {
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
ResponseBody body = response.body();
return body.string();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
// 解析JSON数据的实现
private List<ItemVO> parseJSONData(String responseBodyStr) {
JsonObject responseBodyJSONObject = (JsonObject) new JsonParser().parse(responseBodyStr);
// 响应成功
if (getStatus(responseBodyJSONObject).equals("success")) {
// JSON解析为List
List<ItemVO> jsonToList = parseJSONToList(responseBodyJSONObject);
Log.d(TAG, "响应success");
return jsonToList;
} else {
getResponseErrMsg(ListItemActivity.this, responseBodyJSONObject);
Log.d(TAG, "响应fail,获取商品信息失败");
}
return null;
}
// 获取响应状态
private String getStatus(JsonObject responseBodyJSONObject) {
String status = responseBodyJSONObject.get("status").getAsString();
return status;
}
// 注意这里的返回值不在是单个JsonObject,而是JsonArray
// 强大的Gson可以快速将JsonArray转为List
private List<ItemVO> parseJSONToList(JsonObject responseBodyJSONObject) {
JsonArray jsonArray = responseBodyJSONObject.get("data").getAsJsonArray();
Log.d(TAG, "size: " + jsonArray.size());
Type listType = new TypeToken<List<ItemVO>>() {
}.getType();
List<ItemVO> list = new Gson().fromJson(jsonArray, listType);
return list;
}
在AsyncTask<Void, Void, String>(){ … }中的doInBackground()获取到itemVOList之后,就会执行AsyncTask<Void, Void, String>(){ … }中的onPostExecute(),下面的代码,句句经典!
@Override
protected void onPostExecute(String responseBodyStr) {
if ( !TextUtils.isEmpty(responseBodyStr) ) {
// 使用前先清除数据
itemVOList.clear();
// 解析数据
// 这里不能使用赋值语法,必须使用AddAll,否则无法显示数据
itemVOList.addAll(parseJSONData(responseBodyStr));
// 打乱数据
Collections.shuffle(itemVOList);
// 提示adapter更新数据
itemVOAdapter.notifyDataSetChanged();
}
}
这样,就实现了,获取网络数据,通知适配器数据改变,RecyclerView进行刷新,一系列操作。
十、商品详情页
详情页没有整的像淘宝那样炫酷,而是和注册页相似。
IDEA Tips
- Use the Ctrl+Alt+Shift+U keyboard shortcut to open a UML class diagram in a new editor.
- Use the Ctrl+Alt+U keyboard shortcut to open a UML class diagram in a popup window.
- You can open the database console by clicking or pressing .
- The database console lets you compose and execute SQL statements (Ctrl+Enter), and also analyze the query results.
- You can execute injected SQL statements and the statements in SQL files by pressing Ctrl+Enter. (Alternatively, Alt+Enter | Run Query in Console.)
- The Table Editor provides a GUI for working with table data. In the Database tool window, select the table of interest and press F12.
- The Database tool window provides access to basic info about the database objects (Ctrl+Shift+空格).
Bug收集
由login_db换为miaosha数据库
导致mybatis无法映射而出错
[外链图片转存失败(img-0DKydYtN-1565141994899)(C:\Users\acer\Documents\Markdown文档\Image\1560163327972.png)]