集成算法seetaface6

This commit is contained in:
dengwenjie
2025-03-26 17:09:22 +08:00
parent 45cb7a9edc
commit 72a4c2c058
78 changed files with 1843 additions and 264 deletions

View File

@@ -1,5 +1,8 @@
package cn.smartjavaai.face;
import cn.smartjavaai.face.entity.FaceResult;
import java.io.IOException;
import java.io.InputStream;
/**
@@ -51,4 +54,34 @@ public abstract class AbstractFaceAlgorithm implements FaceAlgorithm{
public float featureComparison(InputStream inputStream1, InputStream inputStream2) throws Exception {
throw new UnsupportedOperationException("默认不支持该功能");
}
@Override
public boolean register(String key, String imagePath) throws Exception {
throw new UnsupportedOperationException("默认不支持该功能");
}
@Override
public boolean register(String key, InputStream inputStream) throws Exception {
throw new UnsupportedOperationException("默认不支持该功能");
}
@Override
public FaceResult search(String imagePath) throws Exception{
throw new UnsupportedOperationException("默认不支持该功能");
}
@Override
public FaceResult search(InputStream inputStream) throws Exception{
throw new UnsupportedOperationException("默认不支持该功能");
}
@Override
public long removeRegister(String... keys) throws Exception{
throw new UnsupportedOperationException("默认不支持该功能");
}
@Override
public long clearFace() throws Exception{
throw new UnsupportedOperationException("默认不支持该功能");
}
}

View File

@@ -3,7 +3,9 @@ package cn.smartjavaai.face;
import ai.djl.MalformedModelException;
import ai.djl.repository.zoo.ModelNotFoundException;
import ai.djl.translate.TranslateException;
import cn.smartjavaai.face.entity.FaceResult;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
@@ -86,4 +88,46 @@ public interface FaceAlgorithm {
*/
float featureComparison(InputStream inputStream1, InputStream inputStream2) throws Exception;
/**
* 注册人脸
* @param key
* @param imagePath
* @return
*/
boolean register(String key, String imagePath) throws Exception;
/**
* 注册人脸
* @param key
* @param inputStream
* @return
*/
boolean register(String key, InputStream inputStream) throws Exception;
/**
* 查询人脸
* @param imagePath
* @return
*/
FaceResult search(String imagePath) throws Exception;
/**
* 查询人脸
* @param inputStream
* @return
*/
FaceResult search(InputStream inputStream) throws Exception;
/**
* 删除已标记人脸
* @param keys
* @return
*/
long removeRegister(String... keys) throws Exception;
/**
* 清空人脸库数据
*/
long clearFace() throws Exception;
}

View File

@@ -2,6 +2,7 @@ package cn.smartjavaai.face;
import cn.smartjavaai.face.algo.FeatureExtractionAlgo;
import cn.smartjavaai.face.algo.RetinaFace;
import cn.smartjavaai.face.algo.SeetaFace6Algo;
import cn.smartjavaai.face.algo.UltraLightFastGenericFace;
import java.util.Map;
@@ -37,6 +38,7 @@ public class FaceAlgorithmFactory {
public static FaceAlgorithm createFaceAlgorithm(ModelConfig config) throws Exception {
Class<?> clazz = registry.get(config.getAlgorithmName().toLowerCase());
if(clazz == null){
System.out.println("No such algorithm: " + config.getAlgorithmName().toLowerCase());
throw new IllegalArgumentException("Unsupported algorithm");
}
FaceAlgorithm algorithm = (FaceAlgorithm) clazz.newInstance();
@@ -114,6 +116,7 @@ public class FaceAlgorithmFactory {
registerAlgorithm("ultralightfastgenericface", UltraLightFastGenericFace.class);
//人脸特征提取
registerAlgorithm("featureExtraction", FeatureExtractionAlgo.class);
registerAlgorithm("seetaface6", SeetaFace6Algo.class);
}
}

View File

@@ -32,6 +32,11 @@ public class ModelConfig {
*/
private String modelPath;
/**
* 人脸库路径
*/
private String faceDbPath;
public String getAlgorithmName() {
return algorithmName;
@@ -72,4 +77,12 @@ public class ModelConfig {
public void setModelPath(String modelPath) {
this.modelPath = modelPath;
}
public String getFaceDbPath() {
return faceDbPath;
}
public void setFaceDbPath(String faceDbPath) {
this.faceDbPath = faceDbPath;
}
}

View File

@@ -25,7 +25,6 @@ import java.util.List;
import java.util.stream.Collectors;
/**
* RetinaFace实现
* @author dwj
*/
public class FeatureExtractionAlgo extends AbstractFaceAlgorithm {
@@ -51,7 +50,7 @@ public class FeatureExtractionAlgo extends AbstractFaceAlgorithm {
* 加载人脸特征提取模型
* @param config
* @throws Exception
*/
*//*
@Override
public void loadFaceFeatureModel(ModelConfig config) throws Exception {
String normalize = mean.stream().map(Object::toString).collect(Collectors.joining(","));
@@ -71,12 +70,12 @@ public class FeatureExtractionAlgo extends AbstractFaceAlgorithm {
}
/**
*//**
* 特征提取
* @param imagePath 图片路径
* @return
* @throws Exception
*/
*//*
@Override
public float[] featureExtraction(String imagePath) throws Exception {
Path imageFile = Paths.get(imagePath);
@@ -85,12 +84,12 @@ public class FeatureExtractionAlgo extends AbstractFaceAlgorithm {
return predictor.predict(img);
}
/**
*//**
* 特征提取
* @param inputStream 输入流
* @return
* @throws Exception
*/
*//*
@Override
public float[] featureExtraction(InputStream inputStream) throws Exception {
Image img = ImageFactory.getInstance().fromInputStream(inputStream);
@@ -98,13 +97,13 @@ public class FeatureExtractionAlgo extends AbstractFaceAlgorithm {
return predictor.predict(img);
}
/**
*//**
* 计算相似度
* @param feature1 图1特征
* @param feature2 图2特征
* @return
* @throws Exception
*/
*//*
@Override
public float calculSimilar(float[] feature1, float[] feature2) throws Exception {
float ret = 0.0f;
@@ -119,13 +118,13 @@ public class FeatureExtractionAlgo extends AbstractFaceAlgorithm {
return (float) ((ret / Math.sqrt(mod1) / Math.sqrt(mod2) + 1) / 2.0f);
}
/**
*//**
* 特征比较
* @param imagePath1 图1路径
* @param imagePath2 图2路径
* @return
* @throws Exception
*/
*//*
@Override
public float featureComparison(String imagePath1, String imagePath2) throws Exception {
float[] feature1 = featureExtraction(imagePath1);
@@ -133,19 +132,19 @@ public class FeatureExtractionAlgo extends AbstractFaceAlgorithm {
return calculSimilar(feature1, feature2);
}
/**
*//**
* 特征比较
* @param inputStream1 图1输入流
* @param inputStream2 图2输入流
* @return
* @throws Exception
*/
*//*
@Override
public float featureComparison(InputStream inputStream1, InputStream inputStream2) throws Exception {
float[] feature1 = featureExtraction(inputStream1);
float[] feature2 = featureExtraction(inputStream2);
return calculSimilar(feature1, feature2);
}
}*/
/*@Override
public float[] recognize(FaceRegion region) {

View File

@@ -0,0 +1,319 @@
package cn.smartjavaai.face.algo;
import cn.smartjavaai.common.entity.Point;
import cn.smartjavaai.common.entity.Rectangle;
import cn.smartjavaai.common.utils.ImageUtils;
import cn.smartjavaai.face.AbstractFaceAlgorithm;
import cn.smartjavaai.face.FaceDetectedResult;
import cn.smartjavaai.face.ModelConfig;
import cn.smartjavaai.face.dao.FaceDao;
import cn.smartjavaai.face.entity.FaceData;
import cn.smartjavaai.face.entity.FaceResult;
import com.seetaface.NativeLoader;
import com.seetaface.SeetaFace6JNI;
import com.seetaface.model.RecognizeResult;
import com.seetaface.model.SeetaImageData;
import com.seetaface.model.SeetaRect;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* SeetaFace6 人脸算法
* @author dwj
*/
@Slf4j
public class SeetaFace6Algo extends AbstractFaceAlgorithm {
private ModelConfig config;
@Override
public void loadModel(ModelConfig config) throws Exception {
this.config = config;
if (NativeLoader.seetaFace6SDK == null) {
synchronized (SeetaFace6JNI.class) {
if(StringUtils.isBlank(config.getModelPath())){
throw new Exception("modelPath is null");
}
//加载依赖库
NativeLoader.loadNativeLibraries(config.getModelPath());
log.info("Loading seetaFace6 library successfully.");
NativeLoader.seetaFace6SDK = new SeetaFace6JNI();
//加载模型
boolean isSuccess = NativeLoader.seetaFace6SDK.initModel(config.getModelPath());
if(!isSuccess){
throw new Exception("seetaFace6模型初始化失败," + config.getModelPath());
}
log.info("Load seetaFace6 model success!");
new Thread(new Runnable() {
public void run() {
try {
log.info("start load faceDb...");
loadFaceDb();
log.info("Load faceDb success!");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
}
@Override
public FaceDetectedResult detect(String imagePath) throws Exception {
// 将图片路径转换为 BufferedImage
BufferedImage image = ImageIO.read(new File(Paths.get(imagePath).toAbsolutePath().toString()));
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
imageData.data = ImageUtils.getMatrixBGR(image);
SeetaRect[] seetaResult = NativeLoader.seetaFace6SDK.detect(imageData);
return convertToFaceDetectedResult(seetaResult);
}
@Override
public FaceDetectedResult detect(InputStream imageInputStream) throws Exception {
BufferedImage image = ImageIO.read(imageInputStream);
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
imageData.data = ImageUtils.getMatrixBGR(image);
SeetaRect[] seetaResult = NativeLoader.seetaFace6SDK.detect(imageData);
return convertToFaceDetectedResult(seetaResult);
}
@Override
public float[] featureExtraction(String imagePath) throws Exception {
BufferedImage image = ImageIO.read(new File(Paths.get(imagePath).toAbsolutePath().toString()));
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
imageData.data = ImageUtils.getMatrixBGR(image);
return NativeLoader.seetaFace6SDK.extractMaxFace(imageData);
}
@Override
public float[] featureExtraction(InputStream inputStream) throws Exception {
BufferedImage image = ImageIO.read(inputStream);
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
imageData.data = ImageUtils.getMatrixBGR(image);
return NativeLoader.seetaFace6SDK.extractMaxFace(imageData);
}
@Override
public float calculSimilar(float[] feature1, float[] feature2) throws Exception {
return NativeLoader.seetaFace6SDK.calculateSimilarity(feature1, feature2);
}
@Override
public float featureComparison(String imagePath1, String imagePath2) throws Exception {
return featureComparison(new FileInputStream(Paths.get(imagePath1).toAbsolutePath().toString()),
new FileInputStream(Paths.get(imagePath2).toAbsolutePath().toString()));
}
@Override
public float featureComparison(InputStream inputStream1, InputStream inputStream2) throws Exception {
BufferedImage image1 = ImageIO.read(inputStream1);
BufferedImage image2 = ImageIO.read(inputStream2);
SeetaImageData imageData1 = new SeetaImageData(image1.getWidth(), image1.getHeight(), 3);
imageData1.data = ImageUtils.getMatrixBGR(image1);
SeetaImageData imageData2 = new SeetaImageData(image2.getWidth(), image2.getHeight(), 3);
imageData2.data = ImageUtils.getMatrixBGR(image2);
//裁剪
byte[][] cropImg1 = NativeLoader.seetaFace6SDK.crop(imageData1);
byte[][] cropImg2 = NativeLoader.seetaFace6SDK.crop(imageData2);
if(cropImg1 == null || cropImg1.length == 0){
throw new Exception("未发现人脸");
}
if(cropImg2 == null || cropImg2.length == 0){
throw new Exception("未发现人脸");
}
BufferedImage cropImage1 = ImageUtils.bgrToBufferedImage(cropImg1[0], 256, 256);
BufferedImage cropImage2 = ImageUtils.bgrToBufferedImage(cropImg2[0], 256, 256);
SeetaImageData cropImageData1 = new SeetaImageData(cropImage1.getWidth(), cropImage1.getHeight(), 3);
cropImageData1.data = ImageUtils.getMatrixBGR(cropImage1);
SeetaImageData cropImageData2 = new SeetaImageData(cropImage2.getWidth(), cropImage2.getHeight(), 3);
cropImageData2.data = ImageUtils.getMatrixBGR(cropImage2);
return NativeLoader.seetaFace6SDK.compare(cropImageData1, cropImageData2);
}
/**
* 转换为FaceDetectedResult
* @param seetaResult
* @return
*/
private FaceDetectedResult convertToFaceDetectedResult(SeetaRect[] seetaResult){
FaceDetectedResult faceDetectedResult = new FaceDetectedResult();
List<Rectangle> RectangleList = new ArrayList<Rectangle>();
List<Double> probabilities = new ArrayList<Double>();
if(seetaResult != null && seetaResult.length > 0){
for(SeetaRect rect : seetaResult){
Rectangle rectangle = new Rectangle();
List<Point> pointList = new ArrayList<>();
pointList.add(new Point(rect.x,rect.y));
pointList.add(new Point(rect.x + rect.width,rect.y));
pointList.add(new Point(rect.x,rect.y + rect.height));
pointList.add(new Point(rect.x + rect.width,rect.y + rect.height));
rectangle.setPointList(pointList);
rectangle.setHeight(rect.height);
rectangle.setWidth(rect.width);
RectangleList.add(rectangle);
probabilities.add(new Double(rect.score));
}
}
faceDetectedResult.setProbabilities(probabilities);
faceDetectedResult.setRectangles(RectangleList);
return faceDetectedResult;
}
@Override
public boolean register(String key, String imagePath) throws Exception {
return register(key, new FileInputStream(Paths.get(imagePath).toAbsolutePath().toString()));
}
@Override
public boolean register(String key, InputStream inputStream) throws Exception {
if(!checkFaceDb()){
throw new Exception("未找到人脸库,无法使用此功能(请检查是否配置人脸库路径)");
}
//裁剪人脸
BufferedImage image = ImageIO.read(inputStream);
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
imageData.data = ImageUtils.getMatrixBGR(image);
byte[][] bytes = NativeLoader.seetaFace6SDK.crop(imageData);
if (bytes == null || bytes.length == 0) {
log.info("register face fail: key={}, error=no valid face", key);
return false;
}
long index = NativeLoader.seetaFace6SDK.registerCroppedFace(bytes[0]);
if (index < 0) {
log.info("register face fail: key={}, index={}", key, index);
return false;
}
//持久化到sqlite数据库
FaceData face = new FaceData();
face.setKey(key);
face.setIndex(index);
face.setImgData(bytes[0]);
new FaceDao(config.getFaceDbPath()).save(face);
return true;
}
public boolean register(String key, FaceData faceData) throws Exception {
long index = NativeLoader.seetaFace6SDK.registerCroppedFace(faceData.getImgData());
if (index < 0) {
log.info("register face fail: key={}, index={}", key, index);
return false;
}
int rows = new FaceDao(config.getFaceDbPath()).updateIndex(index, faceData);
return rows > 0;
}
@Override
public FaceResult search(String imagePath) throws Exception {
return search(new FileInputStream(Paths.get(imagePath).toAbsolutePath().toString()));
}
@Override
public FaceResult search(InputStream inputStream) throws Exception{
if(!checkFaceDb()){
throw new Exception("未找到人脸库,无法使用此功能(请检查是否配置人脸库路径)");
}
BufferedImage image = ImageIO.read(inputStream);
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
imageData.data = ImageUtils.getMatrixBGR(image);
RecognizeResult recognizeResult = NativeLoader.seetaFace6SDK.query(imageData);
return searchFaceDb(recognizeResult);
}
@Override
public long removeRegister(String... keys) throws Exception {
if(!checkFaceDb()){
throw new Exception("未找到人脸库,无法使用此功能(请检查是否配置人脸库路径)");
}
List<Long> list = new FaceDao(config.getFaceDbPath()).findIndexList(keys);
if (list == null) {
return 0;
}
long[] array = list.stream().mapToLong(Long::longValue).toArray();
long rows = NativeLoader.seetaFace6SDK.delete(array);
new FaceDao(config.getFaceDbPath()).deleteFace(keys);
return rows;
}
@Override
public long clearFace() throws Exception{
if(!checkFaceDb()){
throw new Exception("未找到人脸库,无法使用此功能(请检查是否配置人脸库路径)");
}
long rows = NativeLoader.seetaFace6SDK.delete(new long[]{-1});
new FaceDao(config.getFaceDbPath()).deleteAll();
return rows;
}
/**
* 检查是否存在人脸库
* @return
*/
private boolean checkFaceDb(){
if(Objects.nonNull(config) && StringUtils.isNotBlank(config.getFaceDbPath())){
File file = new File(config.getFaceDbPath());
return file.exists() && file.isFile();
}
return false;
}
private FaceResult searchFaceDb(RecognizeResult recognizeResult) throws SQLException, ClassNotFoundException {
if(recognizeResult != null && recognizeResult.index >= 0){
String key = new FaceDao(config.getFaceDbPath()).findKeyByIndex(recognizeResult.index);
return new FaceResult(key, recognizeResult.similar);
}
return null;
}
/**
* 加载人脸库
* @throws SQLException
* @throws ClassNotFoundException
*/
private void loadFaceDb() throws SQLException, ClassNotFoundException {
if(!checkFaceDb()){
log.info("未配置人脸库");
return;
}
//分页查询人脸库
int pageNo = 0, pageSize = 100;
while (true) {
List<FaceData> list = new FaceDao(config.getFaceDbPath()).findFace(pageNo, pageSize);
if (list == null) {
break;
}
list.forEach(face -> {
try {
register(face.getKey(), face);
} catch (Exception e) {
e.printStackTrace();
}
});
if (list.size() < pageSize) {
break;
}
pageNo++;
}
}
}

View File

@@ -0,0 +1,151 @@
package cn.smartjavaai.face.dao;
import cn.smartjavaai.face.entity.FaceData;
import cn.smartjavaai.face.sqllite.RowMapper;
import cn.smartjavaai.face.sqllite.SqliteHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import java.lang.reflect.InvocationTargetException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 人脸库持久层
* @author dwj
*/
@Slf4j
public class FaceDao {
private static final String TABLE_NAME_IMG = "face";
private String dbFilePath;
public FaceDao(String dbFilePath) {
this.dbFilePath = dbFilePath;
}
public void save(FaceData faceData) throws SQLException, ClassNotFoundException {
SqliteHelper sqliteHelper = new SqliteHelper(dbFilePath);
sqliteHelper.executeUpdate("INSERT OR REPLACE INTO " + TABLE_NAME_IMG + " (\"index\",\"key\",\"img_data\",\"width\",\"height\",\"channel\") VALUES (?,?,?,?,?,?)", new Object[]{faceData.getIndex(),faceData
.getKey(), faceData.getImgData(), faceData.getWidth(), faceData.getHeight(), faceData.getChannel()});
}
/**
* 使用index查询key
*
* @param index
* @return
* @throws SQLException
* @throws ClassNotFoundException
*/
public String findKeyByIndex(int index) throws SQLException, ClassNotFoundException {
SqliteHelper sqliteHelper = new SqliteHelper(dbFilePath);
return sqliteHelper.executeQuery("select \"key\" from " + TABLE_NAME_IMG + " where \"index\"=" + index);
}
/**
* 删除全部
*
* @return
* @throws SQLException
* @throws ClassNotFoundException
*/
public long deleteAll() throws SQLException, ClassNotFoundException {
SqliteHelper sqliteHelper = new SqliteHelper(dbFilePath);
long rows = sqliteHelper.executeUpdate("delete from " + TABLE_NAME_IMG);
return rows;
}
/**
* 查询index
*
* @param keys
* @return
* @throws SQLException
* @throws ClassNotFoundException
*/
public List<Long> findIndexList(String... keys) throws SQLException, ClassNotFoundException {
// 使用 Stream API
String inKeys = Arrays.stream(keys)
.map(s -> "'" + s + "'")
.reduce((s1, s2) -> s1 + "," + s2)
.orElse("");
String sql = "select \"index\" from " + TABLE_NAME_IMG + " where \"key\" in (" + inKeys + ")";
log.info("sql{}", sql.toString());
SqliteHelper sqliteHelper = new SqliteHelper(dbFilePath);
return sqliteHelper.executeQuery(sql, new RowMapper<Long>() {
@Override
public Long mapRow(ResultSet rs, int index) throws SQLException {
return rs.getLong(1);
}
});
}
/**
* 删除人脸
* @param keys
* @return
* @throws SQLException
* @throws ClassNotFoundException
*/
public boolean deleteFace(String... keys) throws SQLException, ClassNotFoundException {
String inKeys = Arrays.stream(keys)
.map(s -> "'" + s + "'")
.reduce((s1, s2) -> s1 + "," + s2)
.orElse("");
SqliteHelper sqliteHelper = new SqliteHelper(dbFilePath);
String sql = "delete from " + TABLE_NAME_IMG + " where \"key\" in (" + inKeys + ")";
log.info("sql{}", sql.toString());
sqliteHelper.executeUpdate(sql);
return true;
}
/**
* 分页查询人脸
* @param pageNo
* @param pageSize
* @return
* @throws SQLException
* @throws ClassNotFoundException
*/
public List<FaceData> findFace(int pageNo, int pageSize) throws SQLException, ClassNotFoundException {
String sql = "select \"key\",\"img_data\",\"width\",\"height\",\"channel\" from " + TABLE_NAME_IMG +
" limit " + pageNo * pageSize + "," + pageSize;
SqliteHelper sqliteHelper = new SqliteHelper(dbFilePath);
return sqliteHelper.executeQuery(sql, new RowMapper<FaceData>() {
@Override
public FaceData mapRow(ResultSet rs, int index) throws SQLException {
FaceData face = new FaceData();
face.setKey(rs.getString("key"));
face.setImgData(rs.getBytes("img_data"));
face.setWidth(rs.getInt("width"));
face.setHeight(rs.getInt("height"));
face.setChannel(rs.getInt("channel"));
return face;
}
});
}
/**
* 更新index
* @param index
* @param faceData
* @return
* @throws SQLException
* @throws ClassNotFoundException
*/
public int updateIndex(long index,FaceData faceData) throws SQLException, ClassNotFoundException {
SqliteHelper sqliteHelper = new SqliteHelper(dbFilePath);
return sqliteHelper.executeUpdate("INSERT OR REPLACE INTO " + TABLE_NAME_IMG + " (\"index\",\"key\",\"img_data\",\"width\",\"height\",\"channel\") VALUES (?,?,?,?,?,?)", new Object[]{index,faceData
.getKey(), faceData.getImgData(), faceData.getWidth(), faceData.getHeight(), faceData.getChannel()});
}
}

View File

@@ -0,0 +1,19 @@
package cn.smartjavaai.face.entity;
import lombok.Data;
/**
* 人脸数据
* @author dwj
*/
@Data
public class FaceData {
private String key;
private long index;
private byte[] imgData;
private int width = 256;
private int height = 256;
private int channel = 3;
}

View File

@@ -0,0 +1,22 @@
package cn.smartjavaai.face.entity;
import lombok.Data;
/**
* 人脸查询结果
* @author dwj
*/
@Data
public class FaceResult {
private String key;
private float similar;
public FaceResult() {
}
public FaceResult(String key, float similar) {
this.key = key;
this.similar = similar;
}
}

View File

@@ -0,0 +1,15 @@
package cn.smartjavaai.face.sqllite;
import java.sql.ResultSet;
/**
* ResultSetExtractor
* @author dwj
* @param <T>
*/
public interface ResultSetExtractor<T> {
public abstract T extractData(ResultSet rs);
}

View File

@@ -0,0 +1,14 @@
package cn.smartjavaai.face.sqllite;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* RowMapper
* @author dwj
* @param <T>
*/
public interface RowMapper<T> {
public abstract T mapRow(ResultSet rs, int index) throws SQLException;
}

View File

@@ -0,0 +1,296 @@
package cn.smartjavaai.face.sqllite;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* sqlite帮助类
* @author dwj
*/
@Slf4j
public class SqliteHelper {
private Connection connection;
private Statement statement;
private ResultSet resultSet;
private String dbFilePath;
/**
* 构造函数
* @param dbFilePath sqlite db 文件路径
* @throws ClassNotFoundException
* @throws SQLException
*/
public SqliteHelper(String dbFilePath) throws ClassNotFoundException, SQLException {
this.dbFilePath = dbFilePath;
connection = getConnection(dbFilePath);
}
/**
* 获取数据库连接
* @param dbFilePath db文件路径
* @return 数据库连接
* @throws ClassNotFoundException
* @throws SQLException
*/
public Connection getConnection(String dbFilePath) throws ClassNotFoundException, SQLException {
Connection conn = null;
Class.forName("org.sqlite.JDBC");
conn = DriverManager.getConnection("jdbc:sqlite:" + dbFilePath);
return conn;
}
/**
* 执行sql查询
* @param sql sql select 语句
* @param rse 结果集处理类对象
* @return 查询结果
* @throws SQLException
* @throws ClassNotFoundException
*/
public <T> T executeQuery(String sql, ResultSetExtractor<T> rse) throws SQLException, ClassNotFoundException {
try {
resultSet = getStatement().executeQuery(sql);
T rs = rse.extractData(resultSet);
return rs;
} finally {
destroyed();
}
}
/**
* 执行select查询返回结果列表
*
* @param sql sql select 语句
* @param rm 结果集的行数据处理类对象
* @return
* @throws SQLException
* @throws ClassNotFoundException
*/
public <T> List<T> executeQuery(String sql, RowMapper<T> rm) throws SQLException, ClassNotFoundException {
List<T> rsList = new ArrayList<T>();
try {
resultSet = getStatement().executeQuery(sql);
while (resultSet.next()) {
rsList.add(rm.mapRow(resultSet, resultSet.getRow()));
}
} finally {
destroyed();
}
return rsList;
}
/**
* 简单查询某个字段
* @param sql
* @return
* @throws SQLException
* @throws ClassNotFoundException
*/
public String executeQuery(String sql) throws SQLException, ClassNotFoundException {
try {
resultSet = getStatement().executeQuery(sql);
if(resultSet.next()){
return resultSet.getString(1);
}
return null;
} finally {
destroyed();
}
}
/**
* 执行数据库更新sql语句
* @param sql
* @return 更新行数
* @throws SQLException
* @throws ClassNotFoundException
*/
public int executeUpdate(String sql) throws SQLException, ClassNotFoundException {
try {
int c = getStatement().executeUpdate(sql);
return c;
} finally {
destroyed();
}
}
/**
* 执行多个sql更新语句
* @param sqls
* @throws SQLException
* @throws ClassNotFoundException
*/
public void executeUpdate(String...sqls) throws SQLException, ClassNotFoundException {
try {
for (String sql : sqls) {
getStatement().executeUpdate(sql);
}
} finally {
destroyed();
}
}
/**
* 执行数据库更新 sql List
* @param sqls sql列表
* @throws SQLException
* @throws ClassNotFoundException
*/
public void executeUpdate(List<String> sqls) throws SQLException, ClassNotFoundException {
try {
for (String sql : sqls) {
getStatement().executeUpdate(sql);
}
} finally {
destroyed();
}
}
private Connection getConnection() throws ClassNotFoundException, SQLException {
if (null == connection) connection = getConnection(dbFilePath);
return connection;
}
private Statement getStatement() throws SQLException, ClassNotFoundException {
if (null == statement) statement = getConnection().createStatement();
return statement;
}
/**
* 数据库资源关闭和释放
*/
public void destroyed() {
try {
if (null != statement) {
statement.close();
statement = null;
}
if (null != connection) {
connection.close();
connection = null;
}
if (null != resultSet) {
resultSet.close();
resultSet = null;
}
} catch (SQLException e) {
log.error("Sqlite数据库关闭时异常", e);
}
}
/**
* 执行select查询返回结果列表
*
* @param sql sql select 语句
* @param clazz 实体泛型
* @return 实体集合
* @throws SQLException 异常信息
* @throws ClassNotFoundException 异常信息
*/
public <T> List<T> executeQueryList(String sql, Class<T> clazz) throws SQLException, ClassNotFoundException, IllegalAccessException, InstantiationException {
List<T> rsList = new ArrayList<T>();
try {
resultSet = getStatement().executeQuery(sql);
while (resultSet.next()) {
T t = clazz.newInstance();
for (Field field : t.getClass().getDeclaredFields()) {
field.setAccessible(true);
field.set(t,resultSet.getObject(field.getName()));
}
rsList.add(t);
}
} finally {
destroyed();
}
return rsList;
}
/**
* 执行sql查询,适用单条结果集
* @param sql sql select 语句
* @param clazz 结果集处理类对象
* @return 查询结果
* @throws SQLException
* @throws ClassNotFoundException
*/
public <T> T executeQuery(String sql, Class<T> clazz) throws SQLException, ClassNotFoundException, IllegalAccessException, InstantiationException {
try {
resultSet = getStatement().executeQuery(sql);
T t = clazz.newInstance();
for (Field field : t.getClass().getDeclaredFields()) {
field.setAccessible(true);
field.set(t,resultSet.getObject(field.getName()));
}
return t;
} finally {
destroyed();
}
}
/**
* 执行数据库更新sql语句
* @param tableName 表名
* @param param key-value键值对,key:表中字段名,value:值
* @return 更新行数
* @throws SQLException
* @throws ClassNotFoundException
*/
public int executeInsertOrUpdate(String tableName, Map<String,Object> param) throws SQLException, ClassNotFoundException {
try {
StringBuffer sql = new StringBuffer();
sql.append("INSERT OR REPLACE INTO ");
sql.append(tableName);
sql.append(" ( ");
for (String key : param.keySet()) {
sql.append("'" + key + "'");
sql.append(",");
}
sql.delete(sql.length()-1,sql.length());
sql.append(") VALUES ( ");
for (String key : param.keySet()) {
sql.append("'");
sql.append(param.get(key));
sql.append("',");
}
sql.delete(sql.length()-1,sql.length());
sql.append(");");
log.info("sql{}", sql.toString());
int c = getStatement().executeUpdate(sql.toString());
return c;
} finally {
destroyed();
}
}
public int executeUpdate(String sql, Object[] args) throws SQLException, ClassNotFoundException {
Connection conn = getConnection();
if (args == null || args.length == 0) {
try (Statement stmt = conn.createStatement()) {
return stmt.executeUpdate(sql);
} finally {
destroyed();
}
} else {
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
for (int i = 0; i < args.length; i++) {
stmt.setObject(i + 1, args[i]);
}
return stmt.executeUpdate();
} finally {
destroyed();
}
}
}
}