Druid+JDBC工具类
我只是一个新手,也不怎么会kotlin但是我看教程都很少,讲SQLite-JDBC结合Druid或者说怎么写Durid的配置文件,
我先放工具类
import com.alibaba.druid.pool.DruidDataSourceFactory
import java.io.FileInputStream
import java.io.InputStream
import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.sql.*
import java.util.*
import javax.sql.DataSource
class JdbcUtil {
//数据源连接池对象
private var dataSource: DataSource? = null
//ThreadLocal 对象
private var threadLocal: ThreadLocal<Connection>? = null
private constructor() {
try {
val inputStream: InputStream = FileInputStream("db.properties")
val prop: Properties = Properties()
prop.load(inputStream)
//使用Druid连接池方式
dataSource = DruidDataSourceFactory.createDataSource(prop)
threadLocal = ThreadLocal()
} catch (e: Exception) {
throw RuntimeException("加载配置文件出错!${e.message}")
}
}
@Throws(SQLException::class)
fun getConnection(): Connection? {
//从当前线程获取Connection
var connection: Connection? = this.threadLocal!!.get()
if (connection == null) {
//如果没有就从连接池获取
connection = dataSource?.connection
//添加到ThreadLocal
this.threadLocal?.set(connection)
}
return connection
}
companion object {
@Volatile
private var instance: JdbcUtil? = null
fun getInstance() = instance ?: synchronized(this) {
instance ?: JdbcUtil().also { instance = it }
}
}
/**
* 封装的通用增删改方法
* @param sql 需要操作的SQL语句
* @param params 执行SQL语句时需要的参数
*/
private fun update(sql: String, vararg params: Any = emptyArray()): Int {
var connection: Connection? = null
var statement: PreparedStatement? = null
try {
connection = getConnection()
statement = connection?.prepareStatement(sql)
if (params.isNotEmpty()) {
for (i in params.indices) {
statement?.setObject(i + 1, params[i])
}
}
return statement!!.executeUpdate()
} catch (e: Exception) {
e.printStackTrace()
} finally {
destroy(connection, statement, null)
}
return 0
}
fun executeUpdate(sql: String): Int {
return update(sql)
}
fun executeUpdateBoolean(sql: String): Boolean {
val result: Int = update(sql)
if (result > 0) {
return true
}
return false
}
fun executeUpdate(sql: String, vararg params: Any): Int {
return update(sql, params)
}
fun getPreparedStatement(sql: String): PreparedStatement {
return getConnection()!!.prepareStatement(sql)
}
fun executeUpdateBoolean(sql: String, vararg params: Any): Boolean {
val result: Int = update(sql, params)
if (result > 0) {
return true
}
return false
}
/**
* 查询多条数据的封装方法
* @param clazz 结果的封装对象
* @param sql 要执行的SQL语句
* @param params 执行SQL语句时需要的参数
* @param <T> 消费金融泛型类型
* @return 返回查询的结果集
*/
fun <T> query(sql: String, clazz: Class<T>, vararg params: Any): List<T> {
var connection: Connection? = null
var statement: PreparedStatement? = null
var rs: ResultSet? = null
var entities: List<T>?
try {
connection = getConnection()
statement = connection!!.prepareStatement(sql)
for (i in params.indices) {
statement?.setObject(i + 1, params[i])
}
rs = statement.executeQuery()
entities = mutableListOf()
//获取ResultSet对象来获取元数据信息
val meteData: ResultSetMetaData = rs.metaData
val columnCount: Int = meteData.columnCount
while (rs.next()) {
val entity: T = clazz.getDeclaredConstructor().newInstance()
for (i in 1..columnCount) {
val columnLabel: String = meteData.getColumnLabel(i)
val field: Field = clazz.getDeclaredField(columnLabel)
field.isAccessible = true
field.set(entity, rs.getObject(columnLabel))
}
entities.add(entity)
}
return entities
} catch (e: Exception) {
e.printStackTrace()
throw RuntimeException("查询多条数据时出错,错误信息为:${e.message}")
} finally {
destroy(connection, statement, rs)
}
}
// entity = if (clazz == Int::class.java) {
// val constructor: Constructor<T> = clazz.getDeclaredConstructor(Int::class.javaPrimitiveType)
// constructor.newInstance(rs.getInt(1))
// } else if (clazz == Long::class.java) {
// val constructor: Constructor<T> = clazz.getDeclaredConstructor(Long::class.javaPrimitiveType)
// constructor.newInstance(rs.getLong(1))
// } else {
// throw RuntimeException("参数中能传 int 类型或 long 类型。")
// }
/**
* 查询多条数据的封装方法
* @param clazz 结果的封装对象
* @param sql 要执行的SQL语句
* @param params 执行SQL语句时需要的参数
* @param <T> 消费金融泛型类型
* @return 返回查询的结果
</T> */
fun <T> queryForObject(clazz: Class<T>, sql: String?, vararg params: Any?): T? {
var conn: Connection? = null
var pstm: PreparedStatement? = null
var rs: ResultSet? = null
var entity: T? = null
try {
conn = getConnection()
pstm = conn!!.prepareStatement(sql)
for (i in params.indices) {
pstm.setObject(i + 1, params[i])
}
rs = pstm.executeQuery()
val metaData: ResultSetMetaData = rs.metaData
while (rs.next()) {
entity = when (clazz) {
Int::class -> {
val constructor: Constructor<T> = clazz.getDeclaredConstructor(Int::class.java)
constructor.newInstance(rs.getInt(1))
}
Long::class -> {
val constructor: Constructor<T> = clazz.getDeclaredConstructor(Long::class.java)
constructor.newInstance(rs.getLong(1))
}
else -> {
throw RuntimeException("参数可以转换int或者long")
}
}
}
return entity
} catch (e: Exception) {
e.printStackTrace()
throw RuntimeException("统计查询时出错,错误信息为:" + e.message)
} finally {
destroy(conn, pstm, rs)
}
}
private fun destroy(connection: Connection?, statement: PreparedStatement?, rs: ResultSet?) {
connection?.close()
statement?.close()
rs?.close()
}
}
这两个随便借鉴,我都感觉有问题,我太菜了
import java.io.File
import java.io.FileInputStream
import java.lang.reflect.Field
import java.net.URL
import java.sql.*
import java.util.*
import kotlin.collections.ArrayList
class SQLiteUtils {
//磁盘文件路劲名 db
private var path: String? = null
// 操作链接
private var connection: Connection? = null
//语法执行层
private var statement: Statement? = null
//连接数据库的信息
private var user = ""
private var password = ""
private var url = ""
private var driver = ""
private constructor(path: String) {
this.path = path
connection = getConnection(path)
}
/**
* 把JDBC返回的结果集封装成特定类型
* @Author: fangju
* @Date: 2019/6/15
*/
interface IResultSetHandler<T> {
@Throws(Exception::class)
fun handle(rs: ResultSet): T?
}
//Kotlin 可带参数的检测锁模式
companion object {
@Volatile
private var instance: SQLiteUtils? = null
fun getInstance(path: String) = instance ?: synchronized(this) {
instance ?: SQLiteUtils(path).also { instance = it }
}
}
@Throws(SQLException::class)
fun getStatement(): Statement? {
if (this.statement == null) {
this.statement = this.connection!!.createStatement()
}
return this.statement
}
/**
* 获取Sqlite操作链接
*
* @param path sqlite数据表,为磁盘文件名
* @return
*/
@Throws(SQLException::class, ClassNotFoundException::class)
private fun getConnection(path: String?): Connection? {
var connection: Connection? = null
Class.forName("org.sqlite.JDBC")
if (path!!.endsWith(".db")) {
connection = DriverManager.getConnection("jdbc:sqlite:$path")
}
return connection
}
/**
* 增删改操作
* @param sql 传入的SQL语句
* @param params 可变参数
* @return 操作结果
*/
/* 在kotlin变长参数中 * 叫 spread操作,可以理解为“打散”,“分散”*/
fun executeUpdate(sql: String, vararg params: Any): Int {
var preparedStatement: PreparedStatement? = null
var result: Int = 0
try {
preparedStatement = connection?.prepareStatement(sql)
//给预编译语句赋值
for (i in params.indices) {
preparedStatement?.setObject(i + 1, params[i])
}
//执行SQL语句,获取执行结果
result = preparedStatement!!.executeUpdate()
} catch (e: SQLException) {
e.printStackTrace()
} finally {
preparedStatement?.close()
}
return result
}
fun <T> executeQuery(sql: String, handler: IResultSetHandler<T>, vararg params: Any): T? {
var preparedStatement: PreparedStatement? = null
var rs: ResultSet? = null
var connection: Connection? = null
try {
connection = this.connection
preparedStatement = connection?.prepareStatement(sql)
for (i in params.indices) {
preparedStatement!!.setObject(i + 1, params[i])
}
rs = preparedStatement!!.executeQuery()
return handler.handle(rs)
} catch (e: Exception) {
e.printStackTrace()
} finally {
preparedStatement?.close()
rs?.close()
connection?.close()
}
return null
}
fun executeDML(sql: String, vararg params: Any): Boolean {
var connection: Connection? = null
var statement: PreparedStatement? = null
try {
connection = getConnection(path)
statement = connection?.prepareStatement(sql)
for (i in params.indices) {
statement?.setObject(i + 1, params[i])
}
val resultCode: Int = statement!!.executeUpdate()
if (resultCode > 0) {
return true
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
connection?.close()
statement?.close()
}
return false
}
//获取连接的方法
private fun getConnection(): Connection? {
var connection: Connection? = null
try {
//通过反射获取配置文件位置
val resource: URL? = SQLiteUtils::class.java.getResource("/")
val filePath = File(resource!!.file)
val files: Array<File> = filePath.listFiles()
var configFile: File? = null //配置文件路径
for (file in files) {
if (!file.isDirectory) //不是文件夹
{
if (file.name.equals("mysql.properties")) //文件名为mysql.properties
{
configFile = File(file.path) //确定配置文件
}
}
}
if (configFile == null) { //如果没有mysql.properties配置文件就抛出异常
throw java.lang.Exception("小老弟,检查检查配置文件mysql.properties有没有")
}
// 通过Properties读取配置文件
val properties = Properties()
properties.load(FileInputStream(configFile))
// 获取连接数据库的信息
user = properties.getProperty("user")
password = properties.getProperty("password")
url = properties.getProperty("url")
driver = properties.getProperty("driver")
// 获取数据库连接
Class.forName(driver) //加载驱动
connection = DriverManager.getConnection(url, user, password)
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
return connection
}
/**
* 通用的查询方法
*
* @param sql 要执行的sql语句
* @param clazz 对应的实体类
* @param params 条件参数(?对应的值)
* @param <T> 对应的实体类型
* @return
</T> */
// T表示集合中使用的泛型,Class<T> clazz就是对应的实体类
fun <T> executeDQL(sql: String?, clazz: Class<T>, vararg params: Any?): List<T>? {
var connection: Connection? = null
//PreparedStatement 是 Statement 的子类,它的一大好处是可以防止 SQL 注入
var statement: PreparedStatement? = null
var resultSet: ResultSet? = null
var list: List<T>? = null //存放实体类的集合
try {
connection = getConnection() //获取连接
statement = connection!!.prepareStatement(sql) //执行sql语句
// 设置参数,如果params不为空说明要设置参数
for (i in params.indices) {
//从第一位开始对应赋值
statement.setObject(i + 1, params[i])
}
resultSet = statement.executeQuery() //获取结果集
// 可以通过ResultSet获取当前有那些列
val metaData = resultSet.metaData //元数据,可以通过它获取表中的列
// 获取表中列的总数
val columnCount = metaData.columnCount
// 用来存放我们查询的信息对应的实体类
list = ArrayList()
//判断是否有记录,然后再根据列名获取每列的信息
while (resultSet.next()) { //每次循环获取的是一列数据
//我们现在不能确定传入的实体类,这里用泛型替代,当你传入实体类型时确定
val t = clazz.getDeclaredConstructor().newInstance()
for (i in 1..columnCount) //循环要从1开始
{ //遍历列数,获取每一列的名字
val columnLabel = metaData.getColumnLabel(i) //列名
// 通过列名获取对应列的数据
val `object` = resultSet.getObject(columnLabel)
// 通过列名获取实体类中的属性名
val field: Field = clazz.getDeclaredField(columnLabel)
// 给获取的实体类的属性赋值
field.isAccessible = true //取消安全检查
field.set(t, `object`) //给t对象设置从数据库中查询出来的object值
field.isAccessible = false //开启安全检查
}
list.add(t) //把得到的实体类存入集合
}
} catch (e: java.lang.Exception) {
e.printStackTrace()
} finally {
//关闭连接
close(connection, statement, resultSet)
}
return list //返回实体类集合
}
private fun close(connection: Connection?, statement: Statement?, resultSet: ResultSet?) {
try {
connection?.close()
statement?.close()
resultSet?.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
@Throws(SQLException::class)
fun executeUpdate(sql: String) {
getStatement()?.executeUpdate(sql)
}
@Throws(SQLException::class)
private fun releaseConnection() {
if (this.connection != null || this.connection!!.isClosed) {
this.connection!!.close()
this.connection = null
}
if (this.statement != null) {
this.statement!!.close()
this.statement = null
}
}
}
我是在网上找大佬写的,我只是把代码转成kotlin并改成了单例模式,我也很少写文章,懒。不能保证这个工具类有没有问题,我对kotlin的反射还不是很了解,希望大家可以评论区留言,
db.properties
druid.driverClassName=org.sqlite.JDBC
druid.url=jdbc:sqlite:test.db
druid.validationQuery=SELECT 1
druid.testWhileIdle=true
druid.testOnBorrow=false
druid.testOnReturn=false
如上配置之后,运行项目idea终端会抛出
5月 10, 2022 6:43:09 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
百度了一圈,没什么解决办法,
反正项目运行之后会在C:\Users\WuXiangGuJun\IdeaProjects\SQLite-JDBC\test.db
在idea中双击test.db可以打开数据库文件
这是刚刚创建的数据库,当然这些列是我自己搞的