一、使用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;
}