JDBC的批量新增-大資料處理-結果集的元資料-Class反射-JDBC查詢封裝
一、使用JDBC批量新增
知識點複習:
JDBC的六大步驟 (匯入jar包, 載入驅動類,獲取連線物件, 獲取sql執行器、執行sql與並返回結果, 關閉資料庫連線)
封裝了一個DBUtil 類, 通過讀取屬性檔案的方式獲取 基礎連線資訊。
批量新增: 一次性可執行多個新增記錄 ,將多個sql語句在當前這次連線中執行完畢。
// 設定部門集合 List<Dept> list = new ArrayList<>(); list.add(new Dept(60,"市場部","武漢市")); list.add(new Dept(70,"研發部","武漢市")); list.add(new Dept(80,"教學部","武漢市")); //通過DBUtil獲取資料庫連線 Connection conn = DBUtil.getConn(); String sql="insert into dept(deptno,dname,loc) values (?,?,?)"; //獲取預編譯sql執行器 PreparedStatement ps = conn.prepareStatement(sql); //批量設定該條sql語句的 引數 for(Dept dept : list){ //設定引數 ps.setInt(1,dept.getDeptno()); ps.setString(2,dept.getDname()); ps.setString(3,dept.getLoc()); // 將設定好的引數 先放入批量新增的容器中 ps.addBatch(); // 對於完整的sql語句 可以用有參的 // ps.addBatch(sql); } // 執行sql語句 返回每一行sql語句影響的行數 , int [] counts = ps.executeBatch(); System.out.println("結果長度:"+ counts.length); System.out.println("結果:"+ Arrays.toString(counts)); //關閉sql語句 DBUtil.closeAll(conn,ps,null);
補充知識點: Java使用Excel批量匯入資料 到資料庫中
1、先將Excel通過Java 的 POI框架 讀取資料到記憶體中(List)
2、將List資料 批量新增到 資料庫中。
package com.j2008.jdbc2; import com.j2008.jdbc.Dept; import org.apache.poi.hssf.usermodel.HSSFDateUtil; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.util.CellReference; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import java.io.*; import java.net.URLDecoder; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.*; //import javax.validation.ConstraintViolation; //import javax.validation.Validation; //import javax.validation.Validator; //import javax.validation.ValidatorFactory; //import org.springframework.expression.EvaluationException; /** * ClassName: ExcelUtil * Description: * date: 2020/11/23 9:33 * * @author wuyafeng * @version 1.0 softeem.com */ public class ExcelUtil { // Logger logger = Logger.getLogger(this.getClass()); private String description = "";// 如果校驗失敗,將會給出詳細提示資訊 private Sheet sheet;// execel 物件 private List<String> fieldList;//從xml讀取到的execel表格資訊 private int rowIndex = 0;//當前操作行 private Object objectBean;//每一行資料封裝 private Cell cellStart;// 資料的開始單元格 private Class clazz; //需要封裝的類 // private Validator validator; //hibernate 的校驗器 private String[] fieldVals ; //從execel讀到的某一行的資料 private int fieldSize = 0; //有效資料的列數 private DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); // 針對日期的預設轉換形式 private Expression exp ;//EL 解析器 private ExpressionParser parser; private DecimalFormat df = new DecimalFormat("#"); public String getDescription() { return description; } public Object getObjectBean() { return objectBean; } public ExcelUtil(InputStream execelIS,String xmlFilename, Class clazz,String suffix) throws Exception{ // 開啟execel工作簿 Workbook wb = null; try { if(suffix.equals(".xls")){ wb = new HSSFWorkbook(execelIS); }else if(suffix.equals(".xlsx")){ wb = new XSSFWorkbook(execelIS); } execelIS.close(); } catch (IOException e) { throw new Exception("載入檔案失敗,請確保是否是Execel表格"); } sheet = wb.getSheetAt(0);// 預設取第一個工作簿 //讀配置檔案,獲取所有的屬性列描述 fieldList = this.readFieldsFromXML(getAbsolutePath(xmlFilename)); //個數 fieldSize = fieldList.size(); //找到有效資料的開始單元格 cellStart = this.findStartCell(); if(cellStart == null){ throw new Exception(this.description); } //每次讀取一行execel資料,rowIndex每次增1 rowIndex = cellStart.getRowIndex()+1; //需要封裝的物件類 this.clazz = clazz; //初始化校驗器 // ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); // validator = factory.getValidator(); // //初始化EL解析器 parser = new SpelExpressionParser(); exp = parser.parseExpression("values"); } //是否還有資料 public boolean hasNext(){ Row row = sheet.getRow(rowIndex++); if(row == null) return false; fieldVals = this.getRowValues(row, cellStart.getColumnIndex()); if(Arrays.asList(fieldVals).indexOf("") != -1){ for(String s :fieldVals)//如果每個欄位都是空的,則返回false 否則true if(!s.equals("")) return true; return false; } return true; } //校驗 public boolean validate(){ try { objectBean = Class.forName(clazz.getName()).newInstance(); } catch (Exception e) { // logger.error(e); e.printStackTrace(); } try{ exp.setValue(objectBean, fieldVals);// 給objectBean的屬性賦值 }catch( Exception e){//由於所有的資料型別轉換都有objectBean裡面來處理,故可能有異常,需要進行相應的處理 e.printStackTrace(); return false; } return true; } private String[] getRowValues(Row row,int columnStartIndex){ String[] values = new String[fieldSize]; for(int j = columnStartIndex,t=0;t<fieldSize;j++,t++){ Cell c = row.getCell(j); if(c==null){ values[t] = ""; continue; } switch(c.getCellType()){ case Cell.CELL_TYPE_BLANK: values[t] = ""; break; case Cell.CELL_TYPE_BOOLEAN: values[t] = String.valueOf(c.getBooleanCellValue()); break; case Cell.CELL_TYPE_NUMERIC: if (HSSFDateUtil.isCellDateFormatted(c)) { values[t] = format.format(c.getDateCellValue()); } else if(c.getCellStyle().getDataFormat() == 58) { // 處理自定義日期格式:m月d日(通過判斷單元格的格式id解決,id的值是58) SimpleDateFormat sdf = new SimpleDateFormat("MM/dd"); double value = c.getNumericCellValue(); Date date = org.apache.poi.ss.usermodel.DateUtil .getJavaDate(value); values[t] = sdf.format(date); } else { values[t] = new DecimalFormat("#").format(c.getNumericCellValue()); } break; case Cell.CELL_TYPE_STRING: values[t] = String.valueOf(c.getStringCellValue()); break; default: values[t] = ""; break; } } return values; } // 根據某一個單元格,得到更人性化的顯示,例如“A4” private String getCellRef(Cell cell) { return CellReference.convertNumToColString(cell.getColumnIndex()) + (cell.getRowIndex() + 1); } private String getAbsolutePath(String file) throws Exception { try { file = this.getClass().getClassLoader().getResource(file).getFile(); } catch (NullPointerException e) { throw new Exception("檔案不存在"); } try { // 解決當出現中文路徑時不能解析的bug file = URLDecoder.decode(file, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new Exception( "解碼失敗"); } return file; } private List<String> readFieldsFromXML(String xmlFilename)throws Exception{ SAXReader reader = new SAXReader(); Document document = null; try { document = reader.read(new File(xmlFilename));// 載入配置檔案 } catch (DocumentException e) { e.printStackTrace(); this.description = "IO 異常,讀取配置檔案失敗"; throw new Exception("IO 異常,讀取配置檔案失敗"); } Element root = document.getRootElement(); List<String> fields = new ArrayList<String>(); for (Iterator iter = root.elementIterator("field"); iter.hasNext();) { Element field = (Element) iter.next(); fields.add(field.getTextTrim()); } return fields; } /** * 從execel表中找到資料開始的單元格 * @return */ private Cell findStartCell(){ String firstFieldDesc = this.fieldList.get(0); int endRow = sheet.getLastRowNum()>100?100:sheet.getLastRowNum(); for(int i = 0;i<=endRow;i++){ Row r = sheet.getRow(i); if (r == null) continue; for(int j = 0;j < r.getLastCellNum();j++){ Cell c = r.getCell(j); if( c == null) continue; if(c.getCellType() == Cell.CELL_TYPE_STRING){ if(c.getStringCellValue().trim().equals(firstFieldDesc)){//找到第一個符合要求的欄位,接下來判斷它相鄰的欄位是否都符合要求 if(fieldList.size()>r.getLastCellNum()-j){ this.description = "execel表格與所給配置描述不符,請下載模板檔案"; return null; } for(int k=j+1,t=1;k<=j+fieldList.size()-1;k++,t++){ Cell c2 = r.getCell(k); if(c2 == null){ this.description = "請確保單元格"+this.getCellRef(c2)+"內容是\""+fieldList.get(t)+"\""; return null; } if(c2.getCellType() == Cell.CELL_TYPE_STRING){ if(c2.getStringCellValue().contains(fieldList.get(t))) continue; else{ this.description = "請確保單元格"+this.getCellRef(c2)+"內容是\""+fieldList.get(t)+"\""; return null; } } } return c; }else continue; }else continue; } } this.description = "找不到\""+fieldList.get(0)+"\"這一列"; return null; } public int getRowIndex() { return rowIndex; } public DateFormat getFormat() { return format; } public String createExcelIncludeFailReason(InputStream execelIS,String path,String newExcelName, HashMap<Integer,String> errHash) { FileOutputStream file; try { file = new FileOutputStream(path+"/"+newExcelName); Workbook workbook=this.getWorkbook(execelIS); Sheet s=workbook.getSheetAt(0); //int endRow = sheet.getLastRowNum()>100?100:sheet.getLastRowNum(); int endRow = sheet.getLastRowNum(); for(int i=1;i<=endRow;i++){ if(errHash.get(i)!=null){ Row rowkk = s.getRow(i); Cell error_cell = rowkk.createCell(fieldList.size()); error_cell.setCellValue(errHash.get(i)); } } workbook.write(file); file.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return newExcelName; } public String getFile(InputStream execelIS,String path,String appPath, List<Integer> listF, boolean b) { FileOutputStream file; try { file = new FileOutputStream(path+"/"+appPath); Workbook workbook=this.getWorkbook(execelIS); Sheet s=workbook.getSheetAt(0); Row row=null; //int endRow = sheet.getLastRowNum()>100?100:sheet.getLastRowNum(); int endRow = sheet.getLastRowNum(); for(int i=1;i<=endRow;i++){ Row rowkk = s.getRow(i); Cell info_cell = rowkk.createCell(fieldList.size()); info_cell.setCellValue("sss"); //刪除錯誤行 if(b&& listF.contains(i)){ row=s.getRow(i); if(row!=null)s.removeRow(row); } //刪除正確行 if(!b && !listF.contains(i)){ row=s.getRow(i); if(row!=null)s.removeRow(row); } } workbook.write(file); file.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return appPath; } public Workbook getWorkbook(InputStream execelIS) throws Exception{ Workbook wb = null; try { wb = new HSSFWorkbook(execelIS); } catch (IOException e) { throw new Exception("載入檔案失敗,請確保是否是Execel表格"); } return wb; } /** * 返回List集合 * @param filePath * @param xmlName * @return * @throws Exception */ public static List<Dept> getListByExcel(String filePath,String xmlName ) throws Exception { InputStream is = new FileInputStream(filePath); ExcelUtil eu = new ExcelUtil(is, xmlName, Dept.class,".xls"); Dept cp = null; List<Dept> list = new ArrayList<>(); while (eu.hasNext()) { if (eu.validate()) { // System.out.println( eu.getObjectBean()); cp = (Dept) eu.getObjectBean(); // System.out.println(cp.getDeptno()+"/"+cp.getDname()+ "/"+cp.getLoc() ); list.add(cp); } } return list; } /** * * @param args */ public static void main(String[] args) throws Exception { /** * 使用Excel工具的前提 * 1、定義一個實體類 , 並提供基本的get set方法 和 setValues方法 * 2、提供一個 表頭的 xml檔案。 表頭的名字和 xml中的 <field>部門編號</field> 一致 * 3、 源資料: D:/dept.xls */ List<Dept> list = getListByExcel("D:/dept.xls","importTitle.xml"); for(Dept d : list){ System.out.println(d); } } }
二、JDBC處理大資料型別(Blob ,Text)
將資料庫中儲存 類似圖片,視訊,音訊,小說等這些大資料型別格式是,JDBC如何操作
分兩種情況: 位元組資料和字元資料
1、位元組資料:
原理: 存資料(新增記錄):使用位元組流先 讀取到 記憶體中, 然後在儲存資料中,
取資料(根據id查詢該結果記錄): 先通過jdbc 從結果集中獲取輸入位元組流,將位元組流寫出到磁碟中
新增資料:
/** * 新增一個大資料型別 * @throws Exception */ public static void addImg() throws Exception { //獲取連線 Connection conn = DBUtil.getConn(); //獲取預編譯sql執行器 String sql ="insert into mytest2 values(?,?,?,?,?)"; PreparedStatement ps = conn.prepareStatement(sql); // 設定引數 ps.setInt(1,1002); ps.setString(2,"李四"); ps.setString(3,"男"); ps.setString(4,"自我介紹說明"); // 讀取圖片的源路徑 File file = new File("e:/12.jpg"); FileInputStream fis = new FileInputStream(file); // 需要一個輸入流 // ps.setBlob(5,fis); ps.setBinaryStream(5,fis); //執行sql語句 int count = ps.executeUpdate(); if(count>0){ System.out.println("新增成功"); } //關閉流 DBUtil.closeAll(conn,ps,null); }
查詢資料:
/**
* 根據id 獲取 圖片資訊
* @param id
*/
public static String findById(int id) throws Exception {
// 獲取資料庫連線
Connection conn = DBUtil.getConn();
//獲取預編譯sql執行器
String sql = "select * from mytest2 where id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1,id);
ResultSet rs = ps.executeQuery();
if(rs.next()){
//獲取記錄
String username = rs.getString(2);
//獲取位元組型別 blob
Blob image = rs.getBlob("image");
//獲取位元組流
InputStream is = image.getBinaryStream();
FileOutputStream fos = new FileOutputStream("d:/my.jpg");
// 檔案讀寫
byte [] b = new byte[1024];
int len=0;
while((len = is.read(b))!=-1){
fos.write(b,0,len);
}
System.out.println("檔案儲存成功");
fos.close();
is.close();
// 獲取字元流 寫出到檔案中 text欄位可直接通過String對映,也可以通過字元流
Reader reader = rs.getCharacterStream("introduce");
// 字元輸出流 寫出
Writer writer = new FileWriter("d:/introduce.txt");
char [] c = new char[1024];
int len2 = 0;
while( (len2 = reader.read(c)) !=-1){
writer.write(c, 0 ,len2);
}
writer.close();
reader.close();
return username;
}
DBUtil.closeAll(conn,ps,rs);
return null;
}
三、JDBC元資料
資料庫的元資料表示資料庫資訊 和 結果集資訊的 基礎資訊
1、 資料庫元資料資訊: DataBaseMetaData
方法說明:
//獲取資料庫連線
Connection conn = DBUtil.getConn();
DatabaseMetaData dbMetaData = conn.getMetaData();
System.out.println("資料庫產品名稱:"+ dbMetaData.getDatabaseProductName());
System.out.println("資料庫的主要版本"+dbMetaData.getDatabaseMajorVersion());
System.out.println("資料庫產品資訊的版本"+dbMetaData.getDatabaseProductVersion());
System.out.println("資料庫的url:"+dbMetaData.getURL());
System.out.println("驅動類:"+dbMetaData.getDriverName());
System.out.println("使用者名稱:"+dbMetaData.getUserName());
2、 結果集的元資料 ResultSetMetaData
用於獲取結果集的基礎資訊(處理資料項以外的資訊),例如欄位名稱,欄位個數,欄位的資料型別 ,欄位值
用途: 可以通過欄位資訊 與Java實體類做對映關係 ,實現簡易的ORM(Object Relation Mapping)框架
API :
getColumnCount
getColumnCount() : 獲取欄位的列個數
getColumnLabel(i) : 獲取指定列的欄位名
getColumnType(i):獲取指定列的 類型別 : 常用類:
getColumnName() : 獲取欄位名 與getColumnLabel一樣
1:char; 3:BigDecimal 4:int ;12 varchar 91 :Date
四、反射基礎知識
1、為什麼需要使用反射
由於之前建立物件的過程 ,是已知這個類,然後對類進行編譯,編譯通過之後才可以建立物件, 現在可能出現 “未知的類” 只有“包名+類名” ,在執行期間才知道 該類是否存在,並動態建立該類的物件。 這時 建立物件的過程 可以通過反射的方式 完成。
反射機制的定義: 對於任意一個類,都可以在執行期間,動態的建立該類的物件,能知曉該物件的屬性和方法,並動態呼叫屬性 和方法 ,這個過程就是Java的反射機制
對於任意類 都存在一個該類的Class型別 ,如何獲取類的Classs型別
方式一: Class cls = Class.forName("類的全類名")
方式二: Class cls = 類名.class;
方式三: Class cls = 類的物件名.getClass()
常用API
Construct:
cls.getConstructor(int.class , String.class); 根據引數型別和個數 獲取cls 對應的 構造器物件
Field : 屬性對應的型別
getDeclareFields () : 獲取所有宣告的屬性 (包括 任何修飾符,不包括private修飾)
getFields(): 獲取所有宣告的public修飾的屬性
getDeclareFiled(String name): 根據屬性名獲取屬性物件
getField(String name):根據屬性名獲取屬性物件 (必須是共有的 )
Method : 方法對應的型別
getDeclaredMethods() : 返回當前類的自己宣告的方法
getMethods() :返回所有的方法(包括父類的)
invoke(obj,引數值) :呼叫該方法
getMethod(“方法名” ,引數值):根據方法名返回Method
//獲取所有的方法 返回當前類的自己宣告的方法
Method [] methods = cls.getDeclaredMethods();
System.out.println("當前類自己的方法========");
for(Method m : methods){
//遍歷每一個方法
System.out.println(m.getName()+"------"+ m.getReturnType());
}
Method [] methods2 = cls.getMethods();
System.out.println("它的所有方法=============");
for(Method m : methods2){
System.out.println(m.getName()+"---"+m);
}
// 呼叫屬性 和 方法
// 獲取指定方法的方法
Method m = cls.getMethod("sayHello", String.class);
//呼叫方法 (引數1 : 方法所屬的物件oibj ,引數n : 方法的引數值)
// retObj :方法呼叫的返回值
Object retObj = m.invoke(myObj,"商騰輝");
System.out.println("方法返回值:"+retObj);
使用反射完成物件屬性的拷貝功能
/**
* 物件的屬性值拷貝
* @param fromObj
* @param toObj
*/
public static void copy(Object fromObj ,Object toObj) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//獲取這個物件的Class型別
Class fromCls = fromObj.getClass();
Class toCls = toObj.getClass();
// 獲取源物件的所有屬性名
Field[] fields = fromCls.getDeclaredFields();
for(Field f : fields){
//構建 getXxx的方法
String methodName = "get"+ f.getName().substring(0,1).toUpperCase() +
f.getName().substring(1);
//獲取getXxx方法
Method getMethod = fromCls.getMethod(methodName,null);
// 1 呼叫源物件的get方法,獲取返回值, (如何獲取get方法呢 id-》getId )
Object value = getMethod.invoke(fromObj ,null);
// 2 呼叫目標物件的set方法,設定值 ( set方法的引數型別 就是get方法的返回值型別,引數值就是get方法的返回值)
String methodName2 ="set"+ f.getName().substring(0,1).toUpperCase() +
f.getName().substring(1);
Method setMethod = toCls.getMethod(methodName2,getMethod.getReturnType());
//呼叫set方法
setMethod.invoke(toObj ,value);
System.out.println("屬性賦值成功");
}
}
Student stu1 = new Student(1001,"admin",22,"男");
//目標物件
Student stu2 = new Student();
//複製物件的屬性
copy(stu1,stu2);
System.out.println(stu1);
System.out.println(stu2);
五、JDBC的新增,查詢封裝 (反射知識點)
目前我們只對JDBC的 獲取連線,關閉進行封裝,對於 資料庫表的操作 新增,刪除修改,查詢,根據id查詢 ,都需要單獨寫,這樣太麻煩了, 需要將公有的程式碼 簡化。
1、對新增,刪除,修改方法寫成一個方法
/**
* 對於 新增,刪除,修改方法 不同點在於 sql語句不同
* 引數不同
* @param sql sql語句 insert into dept values(?,?,?)
* @param objs 任意個數任意型別的引數 與sql的?一一對應
* @return
*/
public static int executeUpdate(String sql ,Object ... objs){
// 獲取連線
Connection conn=null;
PreparedStatement ps=null;
// 獲取預編譯sql執行器
try {
conn = DBUtil.getConn();
ps = conn.prepareStatement(sql);
//設定引數
if(objs!=null){
for(int i=0;i<objs.length;i++){
// 引數從1開始,陣列objs的下標從0開始
ps.setObject(i+1 ,objs[i]);
}
}
//執行sql語句
int count = ps.executeUpdate();
return count;
} catch (SQLException e) {
e.printStackTrace();
}finally {
//關閉連線
DBUtil.closeAll(conn,ps,null);
}
return 0;
}
查詢
/**
* 查詢所有的公共方法, 可以自動將結果集對映到 實體類上
* @param sql sql語句
* @param cls 實體類的型別
* @param <T> 實體類的泛型
* @return
*/
public static<T> List<T> listAll(String sql ,Class cls ){
// 儲存結果集的集合list
List<T> list = new ArrayList<T>();
Connection conn=null;
PreparedStatement ps = null;
ResultSet rs=null;
try {
//獲取連線
conn = DBUtil.getConn();
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while(rs.next()){
// 建立這個型別的物件
Object obj = cls.newInstance();
// 獲取這個類的所有欄位個數 獲取每一行的元資料
ResultSetMetaData rsmd = rs.getMetaData();
int clsCount =rsmd.getColumnCount();
for(int i=1;i<=clsCount;i++){
String columnName = rsmd.getColumnLabel(i);
//構建set方法
String setMethod = "set"+columnName.substring(0,1).toUpperCase() + columnName.substring(1);
// 呼叫get方法 獲取返回值型別
String getMethod = "get"+columnName.substring(0,1).toUpperCase() + columnName.substring(1);
Method m1 = cls.getMethod(getMethod,null);
//呼叫方法 方法的引數型別 是 get方法的返回值型別
Method m = cls.getMethod(setMethod, m1.getReturnType());
// 呼叫set方法
m.invoke(obj,rs.getObject(columnName));
}
// 將obj放入 list中
list.add((T)obj);
}
} catch (SQLException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
} finally {
DBUtil.closeAll(conn,ps,rs);
}
return list;
}
/**
* 根據id查詢一行記錄
* @param sql
* @param cls
* @param <T>
* @return
*/
public static<T> T getById(String sql ,Class cls){
List<T> list = listAll(sql,cls) ;
//查詢一個集合,取第一行記錄
return list.size()>0 ? list.get(0) : null;
}