如果需要私服參考這里>maven私服
通常情況下每個(gè)公司會(huì)有自己的*-starter或*-dependency來(lái)統(tǒng)一管理maven依賴版本號(hào)
如:用anyline-simple-dependency來(lái)定義所有外部依賴的版本號(hào)
anyline-simple-start-mvc-mysql繼承anyline-simple-dependency并添加mvc與mysql的依賴,
有需要myql和mvc環(huán)境的項(xiàng)目繼承自anyline-simple-start-mvc-mysql
源碼地址: https://gitee.com/anyline/anyline-simple
添加到DataRow中但不需要參與更新(插入)
row.put("-NAME", "ZH");
添加了空值, 默認(rèn)情況下不參與更新(插入) 如果需要強(qiáng)制參與更新(插入)
row.put("+NAME",null);
如果這樣指定了更新(插入)列,則只會(huì)更新(插入)指定的列,其他列都不會(huì)參與更新(插入)
service.save(row,"NAME");
強(qiáng)制更新(插入)NAME,忽略CODE,其他列不受影響,按默認(rèn)情況處理
service.save(row,"+NAME","-CODE");
插入所有列,更新所有值發(fā)生過(guò)變化的列
service.save(row);
在執(zhí)行數(shù)據(jù)庫(kù)操作時(shí),許多參數(shù)是以String形式獲取到的,無(wú)法參數(shù)識(shí)別數(shù)據(jù)類型,如url中的參數(shù)
還有一些數(shù)據(jù)類型在Java中沒(méi)有對(duì)應(yīng)關(guān)系,如xml/josn/幾何圖形等
而有些數(shù)據(jù)庫(kù)在執(zhí)行SQL時(shí)會(huì)執(zhí)行強(qiáng)類型檢測(cè)。
不像我們平時(shí)用的MySQL在執(zhí)行時(shí)會(huì)進(jìn)行隱式轉(zhuǎn)換,無(wú)論什么類型只要能轉(zhuǎn)換成功就可以執(zhí)行。
而PostgreSQL則要求jdbc參數(shù)與表結(jié)構(gòu)對(duì)應(yīng),如果在varchar列中執(zhí)行int類型會(huì)失敗。
要求開(kāi)發(fā)人員在編碼過(guò)程中記住表數(shù)據(jù)類型,或進(jìn)行類型轉(zhuǎn)換顯示不合理,何況表結(jié)構(gòu)有可能會(huì)變動(dòng)。
可以開(kāi)啟表結(jié)構(gòu)自動(dòng)檢測(cè),在執(zhí)行SQL前把參數(shù)轉(zhuǎn)換成與表結(jié)構(gòu)對(duì)應(yīng)的類型
這樣就可以像MySQL一樣隨意了
ConfigTable.IS_AUTO_CHECK_METADATA = true;可以參考anyline-simple-jdbc-postgresql
表結(jié)構(gòu)類似這樣
ID | BASE_ID | NAME |
1 | NULL | 中國(guó) |
2 | 1 | 山東 |
3 | 2 | 濟(jì)南 |
4 | 2 | 青島 |
5 | 2 | 煙臺(tái) |
6 | 3 | 歷下區(qū) |
7 | 3 | 天橋區(qū) |
8 | 4 | 市南區(qū) |
9 | 4 | 城陽(yáng)區(qū) |
//先取出完整列表
DataSet set = service.querys("SYS_AREA");
//ID:主鍵 BASE_ID:表示上一級(jí)ID的列名
set.dispatchs(true,true, "ID:BASE_ID");
set.dispatchs("children",true,true, "ID:BASE_ID");
//執(zhí)行完成后會(huì)把每個(gè)DataRow中存入當(dāng)前DataRow的下一級(jí)
//這里會(huì)生成多個(gè)樹(shù)型結(jié)構(gòu),一般需要根據(jù)ID取出最頂級(jí)的DataRow set.getRow("ID",1);
get(String key)與Map的get(String key)效果一樣
getString(String key)支持表達(dá)式getString("${ID}-${NAME}")
默認(rèn)的分頁(yè)參數(shù)名比較長(zhǎng):
public static String DEFAULT_KEY_PAGE_ROWS = "_anyline_page_rows" ; //設(shè)置每頁(yè)顯示多少條的key
public static String DEFAULT_KEY_PAGE_NO = "_anyline_page" ; //設(shè)置當(dāng)前第幾頁(yè)的key
public static String DEFAULT_KEY_TOTAL_PAGE = "_anyline_total_page" ; //顯示一共多少頁(yè)的key
public static String DEFAULT_KEY_TOTAL_ROW = "_anyline_total_row" ; //顯示一共多少條的key
public static String DEFAULT_KEY_SHOW_STAT = "_anyline_navi_show_stat" ; //設(shè)置是否顯示統(tǒng)計(jì)數(shù)據(jù)的key
public static String DEFAULT_KEY_SHOW_JUMP = "_anyline_navi_show_jump" ; //設(shè)置是否顯示頁(yè)數(shù)跳轉(zhuǎn)key
public static String DEFAULT_KEY_SHOW_VOL = "_anyline_navi_show_vol" ; //設(shè)置是否顯示每頁(yè)條數(shù)設(shè)置key
public static String DEFAULT_KEY_GUIDE = "_anyline_navi_guide" ; //設(shè)置分頁(yè)樣式的key
public static String DEFAULT_KEY_ID_FLAG = "_anyline_navi_conf_" ; //生成配置文件標(biāo)識(shí)
public String KEY_PAGE_ROWS = DEFAULT_KEY_PAGE_ROWS ; //設(shè)置每頁(yè)顯示多少條的key
public String KEY_PAGE_NO = DEFAULT_KEY_PAGE_NO ; //設(shè)置當(dāng)前第幾頁(yè)的key
public String KEY_TOTAL_PAGE = DEFAULT_KEY_TOTAL_PAGE ; //顯示一共多少頁(yè)的key
public String KEY_TOTAL_ROW = DEFAULT_KEY_TOTAL_ROW ; //顯示一共多少條的key
public String KEY_SHOW_STAT = DEFAULT_KEY_SHOW_STAT ; //設(shè)置是否顯示統(tǒng)計(jì)數(shù)據(jù)的key
public String KEY_SHOW_JUMP = DEFAULT_KEY_SHOW_JUMP ; //設(shè)置是否顯示頁(yè)數(shù)跳轉(zhuǎn)key
public String KEY_SHOW_VOL = DEFAULT_KEY_SHOW_VOL ; //設(shè)置是否顯示每頁(yè)條數(shù)設(shè)置key
public String KEY_GUIDE = DEFAULT_KEY_GUIDE ; //設(shè)置分頁(yè)樣式的key
public String KEY_ID_FLAG = DEFAULT_KEY_ID_FLAG ; //生成配置文件標(biāo)識(shí)
是為了防止跟業(yè)務(wù)參數(shù)重名,但一般項(xiàng)目不會(huì)寫(xiě)這長(zhǎng)啰嗦的參數(shù)名可以在配置文件anyline-navi.xml中修改參數(shù)名
如
<!--當(dāng)前第幾頁(yè)-->
<property key="KEY_PAGE_NO">page</property>或者直接寫(xiě)在Java中,一般寫(xiě)在啟動(dòng)類中
PageNaviConfig.DEFAULT_KEY_PAGE_NO = "page";
PageNaviConfig.DEFAULT_KEY_PAGE_ROWS = "size";
PageNaviConfig.DEFAULT_VAR_PAGE_MAX_VOL = 1000;
PageNaviConfig.DEFAULT_VAR_CLIENT_SET_VOL_ENABLE = true;
//DataRow的key大小寫(xiě)(默認(rèn)大寫(xiě))
DataRow.DEFAULT_KEY_KASE = KeyAdapter.KEY_CASE.LOWER;
//http分頁(yè)參數(shù)-當(dāng)前當(dāng)前第幾頁(yè)(默認(rèn)page)
PageNaviConfig.DEFAULT_KEY_PAGE_NO = "pageNum";
//http分頁(yè)參數(shù)-每頁(yè)多少條(默認(rèn)vol)
PageNaviConfig.DEFAULT_KEY_PAGE_ROWS = "pageSize";
//是否允許前端設(shè)置每頁(yè)多少條(默認(rèn)false)
PageNaviConfig.DEFAULT_VAR_CLIENT_SET_VOL_ENABLE = true;
String ymd = DateBuilder.init().addYear(1).addDay(-1).format("yyyy-MM-dd");
用戶提交的excel內(nèi)容經(jīng)常不固定行列,而是需要根據(jù)單元格內(nèi)容來(lái)確定表頭位置或數(shù)據(jù)起止位置。
如根據(jù)內(nèi)容中包含"結(jié)算日期"的單元格來(lái)確定當(dāng)前行是表頭,下一行是數(shù)據(jù)
根據(jù)內(nèi)容中包含"會(huì)計(jì)年度"的單元格來(lái)確定當(dāng)前數(shù)據(jù)年度。
int[] position = org.anyline.poi.excel.ExcelUtil.position(file.getInputStream(),"時(shí)間.*");
返回第1個(gè)包含"時(shí)間"的單元格位置(下標(biāo)從0開(kāi)始)
支持正則表達(dá)式,
可以根據(jù)返回的坐標(biāo)來(lái)讀取單元格內(nèi)容
String value = ExcelUtil.value(File或InputStream, position[0], position[1]);
以上默認(rèn)第0個(gè)Sheet頁(yè),可以指定Sheet下標(biāo)或名稱
對(duì)于標(biāo)準(zhǔn)的url格式 /list?id=1&id=2
以及標(biāo)準(zhǔn)的json格式 {id:[1,2]}
可以通過(guò)condition("ID:[id]")的形式接收
對(duì)于非標(biāo)準(zhǔn)格式如 /list?id=1,2
可以通過(guò)condition("ID:[split(id)]")的形式接收
最終都是生成SQL WHERE ID IN(1,2)
有些情況下需要對(duì)DataSet分組處理。如:查詢出2000個(gè)手機(jī)號(hào),如果一短信平臺(tái)一次只能發(fā)500個(gè)
List<DataSet> list = set.split(set.size()/500);
for(DataSet items:list) {
List<String> mobiles = items.getDistinctStrings("mobile");
//發(fā)送短信
}
軌跡原始數(shù)據(jù)(保存在列式數(shù)據(jù)庫(kù)或thingsboard平臺(tái)上)
lng=[{"ts":1655007789001,"value":120.1}, {"ts":1655007759002,"value":120.2}],
lat=[{"ts":1655007789001,"value":36.1}, {"ts":1655007759002,"value":36.2}]
通過(guò)org.anyline.thingsboard.util.ThingsBoardClient.getTimeseries()取出列的DataSet結(jié)構(gòu):
[{"TS":1657707789001, "LNG":120.1, "LAT":36.1},
{"TS":1657707759002, "LNG":120.2, "LAT":36.2}]
DataSet轉(zhuǎn)換成地圖軌跡常用的格式:
{
//點(diǎn)位時(shí)間
time: [1657707789001, 1657707759002],
//點(diǎn)位坐標(biāo)
path: [
[120.1, 36.1],
[120.2, 36.2],
]
}
取出時(shí)間 List<Long> times = set.getLongs("TS");
取出坐標(biāo) List<Double[]> points = set.getDoubleArrays("LNG", "LAT");
以anyline-aliyun-sms為例,每個(gè)工具類都會(huì)對(duì)應(yīng)一個(gè)配置類與默認(rèn)實(shí)例化類
如SMSUtil對(duì)應(yīng)SMSConfig與SMSBeanSMSConfig用來(lái)配置帳號(hào)密碼等
SMSBean用來(lái)在系統(tǒng)啟動(dòng)中往Spring上下文中注入一個(gè)默認(rèn)的SMSUtil實(shí)例
SMSUtil就是開(kāi)發(fā)中常用的工具了,如發(fā)送短信、查詢短信接收狀態(tài)、創(chuàng)建短信模板等
其中SMSConfig中的變量可以通過(guò)多種方式設(shè)置
1.配置文件anyline-aliyun-sms.xml(根據(jù)SMSConfig中的靜態(tài)變量CONFIG_NAME = "anyline-aliyun-sms.xml";)
這個(gè)配置文件中可以配置多組帳號(hào)密碼,開(kāi)發(fā)過(guò)程中根據(jù)需要生成針對(duì)不同帳號(hào)的util
一般這樣配置
<configs>
<config key="default">帳號(hào)、密碼等</config>
<config key="instance-oa">帳號(hào)、密碼等</config>
<config key="instance-crm">帳號(hào)、密碼等</config>
</configs>
SMSUtil.getInstance("instance-crm")的方式獲取不同的util
2.如果帳號(hào)無(wú)限多、如開(kāi)發(fā)一個(gè)SAAS平臺(tái),這時(shí)的帳號(hào)密碼會(huì)由不同的租戶或用戶自己設(shè)置,數(shù)據(jù)通常要保存在數(shù)據(jù)中。在運(yùn)行過(guò)程中根據(jù)用戶環(huán)境來(lái)調(diào)用不同的util
在實(shí)例化util前可以通過(guò)
SMSConfig.register("用戶編號(hào)", DataRow)的方式先注冊(cè),其中DataRow中的KEY與配置文件中的KEY相對(duì)應(yīng)
SMSConfig中一般會(huì)提供多個(gè)register的重載
再通過(guò)SMSUtil.getInstance("用戶編號(hào)")的方式獲取util
3.對(duì)于一些簡(jiǎn)單的項(xiàng)目,不想使用配置文件的可以通過(guò)2的方式直接register方式注冊(cè)一個(gè)
也可以設(shè)置SMSConfig中的靜態(tài)變更 DEFAULT_配置文件中的KEY
如DEFAULT_ACCOUNT(對(duì)應(yīng)配置文件中的ACCOUNT)、DEFAULT_PASSWORD(對(duì)應(yīng)配置文件中的PASSWORD)
這樣在系統(tǒng)啟動(dòng)后會(huì)在Spring上下文中默認(rèn)注入一個(gè)SMSUtil實(shí)例
4.現(xiàn)有的項(xiàng)目配置文件中設(shè)置,參考SMSBean中的屬性
@Value("${anyline.aliyun.sms.key:}") private String ACCESS_KEY;
這樣在系統(tǒng)啟動(dòng)后會(huì)在Spring上下文中默認(rèn)注入一個(gè)SMSUtil實(shí)例
5.nacos配置中心
需要添加依賴anyline-nacos
anyline-nacos本身也有配置文件用來(lái)指定NACOS配置中心地址以及namespace/group
可以通過(guò)anyline-nacos.xml配置文件設(shè)置
如果是spring boot項(xiàng)目則按spring boot方式來(lái)配置如 nacos.config.server-addr
如果是spring cloud項(xiàng)目則按spring cloud方式來(lái)配置如 spring.cloud.nacos.config.server-addr
配置好nacos后在nacos中根據(jù) 根據(jù)SMSConfig中的靜態(tài)變量CONFIG_NAME = "anyline-aliyun-sms.xml" 命名nacos中的dataId
都是用來(lái)合并集合, 都有返回值
merge將結(jié)果保存到新集合中
join把其他參數(shù)合并到前一個(gè)參數(shù)中,如果前一個(gè)參數(shù)為null則創(chuàng)建新集合
//以下三種格式,只有cd取值成功時(shí),條件才生效
//當(dāng)cd=1,id=2時(shí) WHERE CODE = 1 OR CODE =2
//當(dāng)cd=null,id=2時(shí) 條件不生效
//當(dāng)cd=1,id=null時(shí) WHERE CODE = 1
service.querys("HR_USER", condition("CODE:cd|id"));
//當(dāng)cd=1時(shí) WHERE CODE =1 OR CODE = 9
//當(dāng)cd=null時(shí) 條件不生效
service.querys("HR_USER", condition("CODE:cd|{9}"));
//當(dāng)cd=1時(shí) WHERE CODE =1 OR CODE IS NULL
//當(dāng)cd=null時(shí) 條件不生效
service.querys("HR_USER", condition("CODE:cd|{NULL}"));
//當(dāng)type=1,dept=null時(shí) WHERE TYPE_CODE = 1
//當(dāng)type=1,dept=2時(shí) WHERE TYPE_CODE =1 OR DEPT_ID =2
//當(dāng)type=null,dept=2時(shí) WHERE DEPT_ID = 2
service.querys("HR_USER", condition("TYPE_CODE:type|DEPT_ID:dept"));
//依次取c1,c2的值,如果c1取值成功則忽略c2,如果都失敗則取默認(rèn)值9
service.querys("HR_USER", condition("CODE:c1:c2:{9}"));
經(jīng)常是因?yàn)闆](méi)有掃描org.anyline包
如果是springboot項(xiàng)目 需要添加注釋
@ComponentScan(basePackages = {"org.anyline","org.anyboot"})
如果用了springboot注意掃描一下org.anyboot
如果是spring-mvc項(xiàng)目 需要添加配置
<context:component-scan base-package="org.anyline"></context:component-scan>
File file = new File(dir,"template_102.xlsx");
ExcelReader reader = ExcelReader.init()
.setFile(file) //文件位置
.setSheet(1) //讀取第1個(gè)sheet(下標(biāo)從0開(kāi)始)
.setHead(0) //表頭在第0行,如果沒(méi)有表頭,結(jié)果集以下標(biāo)作為key
.setData(1) //數(shù)據(jù)從第1行開(kāi)始
.setFoot(1) //到第1行結(jié)束(如果負(fù)數(shù)表示 表尾有多少行不需要讀取)
;
DataSet set = reader.read();
log.warn(set.toJSON());
**json結(jié)構(gòu)相對(duì)簡(jiǎn)單每一對(duì)key value可以直接存入DataRow **
{name:zhang,age:20}
對(duì)應(yīng):
row.put("name","zhang");
row.put("age",20);
但xml比json多了一個(gè)attribute,這個(gè)attribute不直接存入DataRow
<user id="1"><name>zhang</name></user>
對(duì)應(yīng):
user.attr("id","1");
user.put("name","zhang");
同樣取值時(shí)也通過(guò)user.attr("id");
從html中抽取多個(gè)標(biāo)簽,如需要抽取a標(biāo)簽和li標(biāo)簽
最簡(jiǎn)單的是抽取兩次RegularUtil.fetchAllTag(html,"a")RegularUtil.fetchAllTag(html,"li")
但這樣有個(gè)問(wèn)題,兩個(gè)標(biāo)簽的順序會(huì)亂,
如果需要保持順序可以通過(guò)RegularUtil.fetchAllTag(html,"a","li");
但是一定注意:這里的a有可能被包含在li內(nèi)部,這時(shí)li中的a不會(huì)再單獨(dú)抽取
//獲取所有超鏈接(a標(biāo)簽)
/*
* 提取單標(biāo)簽+雙標(biāo)簽
* 不區(qū)分大小寫(xiě)
* 0:全文 1:開(kāi)始標(biāo)簽 2:標(biāo)簽name 3:標(biāo)簽體 (單標(biāo)簽時(shí)null) 4:結(jié)束標(biāo)簽 (單標(biāo)簽時(shí)null)
* 注意標(biāo)簽體有可能是HTML片段,而不是純文本
*/
List<List<String>> list = RegularUtil.fetchAllTag(html,"a");
log.warn("標(biāo)簽數(shù)量:"+list.size());
for(List<String> item:list){
log.warn("全文:"+item.get(0));
log.warn("開(kāi)始標(biāo)簽:"+item.get(1));
log.warn("標(biāo)簽名稱:"+item.get(2));
log.warn("標(biāo)簽體:"+item.get(3));
log.warn("結(jié)束標(biāo)簽:"+item.get(4));
}
//抽取所有 a標(biāo)簽和li標(biāo)簽
//一定注意:這里的a有可能被包含在li內(nèi)部,這時(shí)的a不會(huì)再抽取
list = RegularUtil.fetchAllTag(html,"a","li");
//放多情況下我們并不需要復(fù)雜的標(biāo)簽內(nèi)容,只需要截取幾個(gè)關(guān)鍵字
//如提取商品名稱和商品價(jià)格,而這兩個(gè)值有可能是根其他內(nèi)容混在一塊的
//如以下這段源碼
String html ="<div class='title' data-product='1001'>商品名稱(限時(shí))</div>"
+"<div class='price'>一個(gè)貨幣符號(hào):100.00</div>";
//這時(shí)可以通過(guò)字符串截取的方式提取出價(jià)格
//第0個(gè)參數(shù):源數(shù)據(jù)
//第1個(gè)到倒數(shù)第2個(gè)參數(shù):100.00(就是我們要提取的價(jià)格) 之前出現(xiàn)的關(guān)鍵字
//最后1個(gè)參數(shù):100.00之后出現(xiàn)的第1個(gè)關(guān)鍵字
//參數(shù)順序: 源碼,k1,k2,k3,kn-1,內(nèi)容,kn
String price = RegularUtil.cut(html, "price",":","</div>");
log.warn("價(jià)格:{}",price);
//許多情況下price有可能在源碼中出現(xiàn)多次,這時(shí)需要多個(gè)關(guān)鍵字的組合來(lái)確認(rèn)100.00的位置
html = DateUtil.format("yyyy-MM-dd")+ "<div class='title' data-product='1001'>商品名稱(限時(shí))</div>" +
"div class='src-price price'></div>" +
"<div class='price'>一個(gè)貨幣符號(hào):100.00</div>元";
price = RegularUtil.cut(html,"src-price","price", "price",":","</div>");
log.warn("價(jià)格:{}",price);
//如果需要提取的內(nèi)容在最后 如上面的單位:元
String unit = RegularUtil.cut(html,"src-price","price", "price",":","</div>", RegularUtil.TAG_END);
log.warn("單位:{}", unit);
//同樣的如果需要提取的內(nèi)容在最開(kāi)始位置 如上面的日期
String ymd = RegularUtil.cut(html, RegularUtil.TAG_BEGIN, "<");
log.warn("日期:{}", ymd);
TableBuilder builder = TableBuilder.init()
.setDatas(set) //設(shè)置數(shù)據(jù)源
.setFields( //需要導(dǎo)出的列
"{num}(EMPLOYEE_NM)" //{num}表示序號(hào),(DEPARTMENT_NM)表示根據(jù)哪一列計(jì)算序號(hào),這里部門名稱需要分組合并,所以num不是按行計(jì)算
,"DEPARTMENT_NM"
,"EMPLOYEE_NM"
,"YM"
,"BASE_PRICE")
.addUnion( //需要合并的列
"DEPARTMENT_NM" //如果部門名稱相同則合并
,"EMPLOYEE_NM(DEPARTMENT_NM)"
,"YM(DEPARTMENT_NM)" //如果月份相同則合并,前提是部門已經(jīng)合并
)
.setReplaceEmpty("/") //如果值為空則以/代替
.addIgnoreUnionValue("/") //不參與合并的值
.setCellBorder(true) //設(shè)置默認(rèn)邊框
.setMergeCellHorizontalAlign("center") //設(shè)置合并的列 水平對(duì)齊方式
.setMergeCellVerticalAlign("top") //設(shè)置合并的列 垂直對(duì)齊方式
.setEmptyCellHorizontalAlign("center") //設(shè)置空單元格 水平對(duì)齊方式(為空時(shí)有可能需要替換成其他值)
.setEmptyCellVerticalAlign("top") //設(shè)置空單元格 垂直對(duì)齊方式
.setHorizontalAlign("YM","center") //設(shè)置月份列 水平對(duì)齊方式
.setVerticalAlign("middle") //設(shè)置所有數(shù)據(jù)單元格 垂直對(duì)齊方式
.setLineHeight("50px") //設(shè)置數(shù)據(jù)區(qū)域行高
.setWidth("YM","200px") //設(shè)置月份列 寬度
;
Table table = builder.build();
File file = new File(dir, "export_table.xlsx");
ExcelUtil.export(file, table);
DataSet set = service.querys("V_HR_SALARY","YYYY:"+ (DateUtil.year()-1), "ORDER BY EMPLOYEE_ID, YM");
//最簡(jiǎn)單的導(dǎo)出一個(gè)列表,如果文件已存在,則在原文件內(nèi)容基礎(chǔ)上插入行
File file = new File(dir,"export_list.xlsx");
//1表示從第1行插入,如果原來(lái)文件有內(nèi)容,則下移
//{num}表示第幾行,下標(biāo)從1開(kāi)始
//這里支持復(fù)合KEY
ExcelUtil.export(file,1, set,"序號(hào):{num}","部門:DEPARTMENT_NM","姓名:EMPLOYEE_NM","月份:YM","底薪:{BASE_PRICE}+{REWARD_PRICE}");
//如果表頭、表尾格式比較復(fù)雜,可先創(chuàng)建模板,再根據(jù)模板導(dǎo)出
File template = new File(dir,"template.xlsx");//這里是一個(gè)模板文件
//根據(jù)模板導(dǎo)出時(shí)就不需要指定表頭了,只要對(duì)應(yīng)好順序,并計(jì)算好從哪一行開(kāi)始寫(xiě)入
if(template.exists()) {
ExcelUtil.export(template, file, 1, set, "{num}", "DEPARTMENT_NM", "EMPLOYEE_NM", "YM", "{BASE_PRICE}+{REWARD_PRICE}");
}
File file = new File(dir,"export_table.xlsx");
List list = ExcelUtil.read(file); //默認(rèn)讀取第0個(gè)sheet從第0行開(kāi)始
list = ExcelUtil.read(file,1,3); //讀取第1個(gè)sheet從第3行讀取
//遇到合并單元格的,將拆分開(kāi)未合并前的狀態(tài),拆分后補(bǔ)上每個(gè)單元格的值
//返回的是一個(gè)二維數(shù)組
//為了操作方便可以把返回值轉(zhuǎn)換成DataSet,DataSet中的條目(DataRow)以excel列下標(biāo)作為屬性key
DataSet set = new DataSet(list);
//默認(rèn)情況下導(dǎo)出的第0個(gè)sheet也就是sheet1
//如果要導(dǎo)出到多個(gè)sheet需要執(zhí)行多次export導(dǎo)出到同一個(gè)文件,每次執(zhí)行時(shí)指定sheet名稱或下標(biāo)
Table table = null;
File file = new File(dir, "export_sheet.xlsx");
ExcelUtil.export(file, "shet1", table);
ExcelUtil.export(file, "shet2", table);
DataSet set = service.query("HR_USER");
set.distinct("DEPARTMENT_NM"); //這里返回的還是人員列表,但一個(gè)部門只返回一個(gè)
.concat("DEPARTMENT_NM"); //這里返回String并以逗號(hào)分隔:部門A,部門B
List<String> departments = set.getDistinctStrings("DEPARTMENT_NM"); //這里返回一個(gè)不重復(fù)的部門名稱List
TableBuilder.init()..setCellBorder(true)
有些情況下,需要把空值替換成其他固定的符號(hào)如(/)
這時(shí)可以設(shè)置這些單元格的對(duì)齊方式
TableBuilder.init()
.setEmptyCellVerticalAlign("top")
.setEmptyCellHorizontalAlign("center")
TableBuilder.init()
.setHorizontalAlign("CHECK_CODE", "center")
.setVerticalAlign("CHECK_CODE", "top")
TableBuilder.init()
.setMergeCellHorizontalAlign("center") //設(shè)置合并單元格水平對(duì)齊方式
.setMergeCellVerticalAlign("center") //設(shè)置合并單元格垂直對(duì)齊方式
在導(dǎo)出excel時(shí)有可能不是每行一個(gè)序號(hào),而是每組一個(gè)序號(hào),如按部門分組,每個(gè)部門一個(gè)序號(hào)
1 | 人事部 | 張三 | 20 |
張三三 | 22 | ||
2 | 財(cái)務(wù)部 | 李四 | 25 |
王五 | 26 | ||
王五五 | 20 |
這時(shí)num需要部門列來(lái)合并單元格
TableBuilder builder = TableBuilder.init()
.setDatas(set) //設(shè)置數(shù)據(jù)源
.setFields("{num}(DEPARTMENT_NAME)"
,"DEPARTMENT_NAME"
,"USER_NAME"
,"USER_AGE") //設(shè)置需要導(dǎo)出的屬性(列)
.addUnion("DEPARTMENT_NAME"); /設(shè)置需要合并行的列,如果年相同的合并,月相同的合并(前提是年相同)
File file = new File("模板地址");
ExcelUtil.export(file, "sheet名稱", 2, builder.build()); //從第2行插入(根據(jù)表頭行數(shù))
導(dǎo)出excel時(shí)如果每行需要一個(gè)序號(hào)可以用{num}來(lái)代替屬性名,如
export(file, list, "序號(hào):{num}","姓名:NAME","年齡:AGE")
1 | 張三 | 20 |
2 | 李四 | 22 |
3 | 王五 | 25 |
DataRow中g(shù)et是覆蓋了父類Map的get
getString在get的基礎(chǔ)上增加了復(fù)合KEY的支持,如getString("{ID}/{CODE}")
許多情況下需要從DataRow中取多個(gè)值合并顯示。如導(dǎo)出excel時(shí)地址列需要合并省市區(qū)詳細(xì)地址
DataRow可以取多個(gè)值拼接,但DataSet則需要遍歷,非常麻煩
DataRow提供了復(fù)合KEY取值的函數(shù)
如{ID:1,CODE:A01,NAME:張三}
row.getString("{ID}-{CODE}")可以取出 1-A01row.getString("編號(hào):{CODE};姓名:{NAME}")可以取出 編號(hào):A01;姓名:張三
需要注意的是如果其中一個(gè)KEY取值為null 或 KEY不存在則以""代替,而不是"null"
類似的在導(dǎo)出EXCEL時(shí)指定需要導(dǎo)出的列時(shí)也可以使用復(fù)合KEY
將key列中的oldChar替換成newChar
public DataSet replace(String key, String oldChar, String newChar)
將所有列中的oldChar替換成newChar
public DataSet replace(String oldChar, String newChar)
將所有列中的空值替換成value
public DataSet replaceEmpty(String value)
根據(jù)集合數(shù)據(jù)源生成checkbox時(shí),默認(rèn)情況下會(huì)取集合中條目的ID值作為input的value值,取條目的NM值作為label的標(biāo)簽體
但數(shù)據(jù)源經(jīng)常是從數(shù)據(jù)庫(kù)中查詢出來(lái)的,列中并不一定有ID和NM,也有可能是CODE,TITLE或其他情況
這時(shí)需要顯式指定value key與text key
<al:checkbox data="${set }" valueKey="CODE" textKey="TITLE" />
public static boolean export(File file, String sheet, int rows, DataSet set, String ... configs)如果文件存在則在當(dāng)前文件中插入數(shù)據(jù),如果文件不存在則新創(chuàng)建文件
這里的sheet如果在file中已存在,則往這個(gè)sheet中插入數(shù)據(jù),如果不存在則新創(chuàng)建sheet再繼續(xù)插入數(shù)據(jù),其他重載函數(shù)規(guī)則相同。
如
File file = new File();
export(file, "s1",0, set, "ID"); //這一行執(zhí)行完成后在文件中有一個(gè)sheet(s1)
export(file, "s2",0, set, "ID");//這一行執(zhí)行完后文件中有兩個(gè)sheet(s1,s2)
def是指如果沒(méi)有輸入值,則取默認(rèn)值,默認(rèn)值同樣會(huì)執(zhí)行格式化與其他運(yùn)算
evl/nvl是指如果結(jié)果為empt(null),則直接輸出evl/nvl,不會(huì)執(zhí)行格式化,不會(huì)執(zhí)行運(yùn)算
value與body為空的情況下
如果nvl=true則顯示當(dāng)前日期,如果nvl=false則不輸出
如果nvl=其他值,則直接輸出nvl
如果沒(méi)有指定value,body,nvl都為空,同時(shí)def有值,則輸入def
需要注意的是def與nvl=true時(shí),日期都會(huì)參與格式化與運(yùn)算(如增加一天),而nvl=其他值時(shí),直接原樣輸出evl并不執(zhí)行格式化與其他運(yùn)算
有一種典型的場(chǎng)景AJAX分頁(yè),
用戶第一次打開(kāi)列表頁(yè)時(shí)默認(rèn)顯示第1頁(yè)。
用戶在列表頁(yè)中切換到第2頁(yè)后,在第2頁(yè)中打開(kāi)一個(gè)條目的明細(xì)。操作完明細(xì)后退。返回到到列表
這時(shí)列表頁(yè)需要直接顯示第2頁(yè)數(shù)據(jù)。
如何區(qū)分是后退過(guò)來(lái)的還是頁(yè)面刷新過(guò)來(lái)的。navi是在列表頁(yè)中存放了一個(gè)隱藏的input在切換頁(yè)數(shù)后修改這個(gè)input
刷新過(guò)來(lái)的頁(yè)面這個(gè)input值為空,而后退過(guò)來(lái)的這個(gè)input值為2
根據(jù)瀏覽器加載機(jī)制,在頁(yè)面加載完成之后才會(huì)給input賦值。所以在頁(yè)面加載過(guò)程中取不到這個(gè)input值。
可以把需要取input值的代碼放在setTimeout(function(){取值},0);
public static boolean append(File item, File zip, String dir, String comment)
public static boolean append(File item, File zip)
//zip壓縮包中的item文件替換成content文件
public static void replace(File zip, File content, String item)
//zip壓縮包中的item文件替換成content內(nèi)容
public static void replace(File zip, String content, String item)
//讀取zip壓縮包中的item文件內(nèi)容
public static InputStream read(File zip, String item)
以String格式返回
public static String read(File zip, String item, String encode)
刪除zip中的item文件(含目錄)
public static boolean remove(File zip, String item)
ZipUtil.remove(new File(”users.zip“),"user1.xml")
ZipUtil.remove(new File(”users.zip“),"list/user1.xml")
把item文件壓縮到zip文件中
如果item是一個(gè)目錄,則遞歸item中所有文件
如果zip已存在則覆蓋原文件
public static boolean zip(File item, File zip)
ZipUtil.zip(new File("D:\\users\\user.xml"), new File("D:\\user.zip"));
ZipUtil.zip(new File("D:\\users"), new File("D:\\user.zip"));
把item文件壓縮到zip文件中的dir目錄
public static boolean zip(File item, File zip, String dir)
public static boolean zip(Collection<file> items, File zip)
把items文件集合壓縮到zip文件中,以key作為壓縮目錄
public static boolean zip(Map<string,file> items, File zip)
//替換zip壓縮包中的item文件
public static void replace(File zip, File content, String item)
public static void replace(File zip, String content, String item)
//刪除zip壓縮包中的item文件
public static boolean remove(File zip, String item)
//讀取zip壓縮包中的item文件內(nèi)容
public static InputStream read(File zip, String item)
public static String read(File zip, String item, String encode)
</string,file></file>
更多常用示例在后文中有所展現(xiàn),囿于文章篇幅所限,此處不過(guò)多做介紹!
更多建議: