学习链接:Java 视频教程全集
课件链接:Java课件
声明:全是本人边学习边手打的,希望对大家有帮助。
普通字符:匹配与之相同的一个字符
简单的转义字符:
标准字符集合
自定义字符集合
[\d.\-+]
将匹配:数字、小数点、+、-量词 | Quantifier
字符边界(零宽)
IGNORECASE:忽略大小写模式
SINGLELINE:单行模式
MULTILINE:多行模式
选择符和分组
反向引用(\nnm)
预搜索(零宽断言)
开发环境
JAVA相关类位于java.util.regex包下面
类Pattern:
Pattern p = Pattern.compile(r, int);
类Matcher:
Matcher m = p.matcher(str);
测试
匹配整个正则表达式
package com.sxt.regex;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: Demo01.java
* @time: 2020/3/3 14:10
* @desc: |
*/
public class Demo01 {
public static void main(String[] args){
// 在这个字符串:asdfsadf2323,是否符合制定的正则表达式:\w+
Pattern p = Pattern.compile("\\w+");
// 创建Matcher对象
Matcher m = p.matcher("asdfsadf@@2323");
// 尝试将整个字符序列与该模式匹配
// boolean yo = m.matches();
// 该方法扫描输入的序列,查找与该模式匹配的下一个子序列
while(m.find()){
// group()和group(0)都是匹配整个表达式的子字符串
System.out.println(m.group());
System.out.println(m.group(0));
}
}
}
测试正则表达式分组的处理
package com.sxt.regex;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @desc: | 测试正则表达式分组的处理
*/
public class Demo02 {
public static void main(String[] args) {
// 在这个字符串:asdfsadf2323,是否符合制定的正则表达式:\w+
Pattern p = Pattern.compile("([a-z]+)([0-9]+)");
// 创建Matcher对象
Matcher m = p.matcher("asdfsa12**asd233**dsd11");
// 尝试将整个字符序列与该模式匹配
while (m.find()) {
// group()和group(0)都是匹配整个表达式的子字符串
System.out.println("start---");
System.out.println("满足整个表达式的子字符串:");
System.out.println(m.group());
System.out.println("满足第1个括号中表达式的字符串:");
System.out.println(m.group(1));
System.out.println("满足第2个括号中表达式的字符串:");
System.out.println(m.group(2));
}
}
}
测试正则表达对象替换操作
package com.sxt.regex;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @desc: | 测试正则表达对象替换操作
*/
public class Demo03 {
public static void main(String[] args) {
// 在这个字符串:asdfsadf2323,是否符合制定的正则表达式:\w+
Pattern p = Pattern.compile("[0-9]");
// 创建Matcher对象
Matcher m = p.matcher("asdfsa12**asd233**dsd11");
// 替换
String newStr = m.replaceAll("#");
System.out.println(newStr);
}
}
测试正则表达对象分割字符串的操作
package com.sxt.regex;
import java.util.Arrays;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @desc: | 测试正则表达对象分割字符串的操作
*/
public class Demo04 {
public static void main(String[] args) {
String str = "asdfsa12asd233dsd11";
// 切割
String[] arrs = str.split("\\d+");
System.out.println(Arrays.toString(arrs));
}
}
package com.sxt.regex;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: WebSpider.java
* @time: 2020/3/4 17:29
* @desc: |网络爬虫取数据
*/
public class WebSpider {
public static void main(String[] args) {
String url = "http://www.163.com";
String destStr = getURLContent(url);
// 取到的超链接的整个内容
// Pattern p = Pattern.compile("");
// 取到的超链接的地址
// Pattern p = Pattern.compile("href=\"(.+?)\"");
// 注意:上述?是非贪婪模式
// Matcher m = p.matcher(destStr);
// while (m.find()) {
// System.out.println(m.group());
// System.out.println("-----");
// System.out.println(m.group(1));
// }
List<String> result = getMatherSubstrs(destStr, "href=\"(http://[\\w\\s./]+?)\"");
for (String temp : result) {
System.out.println(temp);
}
}
public static String getURLContent(String loc) {
/*获得url对应的网页源码内容*/
StringBuilder sb = new StringBuilder();
try {
URL url = new URL(loc);
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), Charset.forName("gbk")));
String temp = "";
while ((temp = reader.readLine()) != null) {
sb.append(temp);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
public static List<String> getMatherSubstrs(String destStr, String regexStr) {
// 取到的超链接地址
Pattern p = Pattern.compile(regexStr);
Matcher m = p.matcher(destStr);
List<String> result = new ArrayList<>();
while (m.find()) {
result.add(m.group(1));
}
return result;
}
}
JDBC(Java Database Connection)为java开发者使用数据库提供了统一的编程接口,它由一组java类和接口组成。是java程序与数据库系统通信的标准api。JDBC API使得开发人员可以使用纯java的方式来连接数据库,并执行操作。
访问数据库流程
Driver接口
Class.forName("com.mysql.jdbc.Driver");
Class.forName("oracle.jdbc.driver.OracleDriver")
DriverManager接口
Connection接口
Connection con = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password");
Connection con = DriverManager.gtConnection("jdbc:oracle:thin:@host:port:database", "user", "password");
JDBC详细操作
连接测试
报错参考连接:解决方案
package com.sxt.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: Demo01.java
* @time: 2020/3/5 12:48
* @desc: | 测试跟数据库建立连接
* 如果报错:参考连接:https://www.cnblogs.com/cn-chy-com/p/10145690.html
*/
public class Demo01 {
public static void main(String[] args) {
try {
// 加载驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
long start = System.currentTimeMillis();
// 建立连接(连接对象内部其实包含了Socket对象,是一个远程的连接。比较耗时!这是Connection对象管理的一个要点!)
// 真正开发中,为了提高效率,都会使用连接池来管理连接对象!
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
long end = System.currentTimeMillis();
System.out.println(conn);
System.out.println("建立连接耗时:" + (end - start) + "ms毫秒");
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
Statement接口
Statement测试执行sql语句以及sql注入问题
package com.sxt.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: Demo02.java
* @time: 2020/3/5 12:48
* @desc: | 测试执行sql语句以及sql注入问题
*/
public class Demo02 {
public static void main(String[] args) {
try {
// 加载驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
// 建立连接(连接对象内部其实包含了Socket对象,是一个远程的连接。比较耗时!这是Connection对象管理的一个要点!)
// 真正开发中,为了提高效率,都会使用连接池来管理连接对象!
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
Statement stmt = conn.createStatement();
String sql = "insert into t_user (username, pwd, regTime) values ('赵六', 6666, now())";
stmt.execute(sql);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
sql注入问题:若要根据id删除一行记录,很容易出现数据库危险,比如要删除id=5的记录,传入的时候为id = 5 or 1 = 1
,最终导致数据库都被删除。
测试PreparedStatement的用法
package com.sxt.jdbc;
import java.sql.*;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: Demo01.java
* @time: 2020/3/5 12:48
* @desc: | 测试PreparedStatement的基本用法
*/
public class Demo03 {
public static void main(String[] args) {
try {
// 加载驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
// ?是占位符
String sql = "insert into t_user (username, pwd, regTime) values (?, ?, ?)";
PreparedStatement ps = conn.prepareStatement(sql);
// 参数索引是从1开始计算,而不是0
// ps.setString(1, "傻瓜");
// ps.setString(2, "12345");
// 还可以不管类型直接setObject
// ps.setObject(1, "傻瓜2");
// ps.setObject(2, "12344");
// 设置时间:注意该时间的格式应该是java.sql.Date
ps.setObject(1, "傻瓜3");
ps.setObject(2, "12343");
ps.setObject(3, new java.sql.Date(System.currentTimeMillis()));
System.out.println("插入一行记录");
// 返回是否有结果集
// ps.execute();
// 返回更新的行数
int count = ps.executeUpdate();
System.out.println(count);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
关闭顺序:resultset–>statement–>connection
测试ResultSet结果集的用法
package com.sxt.jdbc;
import java.sql.*;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @desc: | 测试ResultSet结果集的用法
* 记得要关闭打开的接口
*/
public class Demo04 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 加载驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
// ?是占位符
String sql = "select id, username, pwd from t_user where id>?";
ps = conn.prepareStatement(sql);
// 把id>2的记录都取出来
ps.setObject(1, 2);
rs = ps.executeQuery();
while (rs.next()) {
// 数字代表哪一列
System.out.println(rs.getInt(1) + "-->" + rs.getString(2) + "-->" + rs.getString(3));
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
// 一定要将三个try catch分开写
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
Batch批处理,尽量使用Statement而不是PreparedStatement
package com.sxt.jdbc;
import java.sql.*;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @desc: | 批处理
*/
public class Demo05 {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 加载驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
// 设为手动提交
conn.setAutoCommit(false);
long start = System.currentTimeMillis();
stmt = conn.createStatement();
for (int i = 0; i < 20000; i++) {
stmt.addBatch("insert into t_user (username, pwd, regTime) values ('li'" + ", 666666, now())");
stmt.executeBatch();
}
// 提交事务
conn.commit();
long end = System.currentTimeMillis();
System.out.println("插入20000条数据,耗时(毫秒):" + (end - start));
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
// 一定要将三个try catch分开写
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
事务的基本概念:一组要么同时执行成功,要么同时执行失败的SQL语句。是数据库操作的一个执行单元。
事务开始于:
事务结束于:
事务的四大特性(ACID)
测试事务的基本用法
package com.sxt.jdbc;
import java.sql.*;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @desc: | 测试事务的基本用法
*/
public class Demo06 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps1 = null;
PreparedStatement ps2 = null;
try {
// 加载驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
// JDBC默认是自动提交
conn.setAutoCommit(false);
ps1 = conn.prepareStatement("insert into t_user (username, pwd) values (?, ?)");
ps1.setObject(1, "狗子");
ps1.setObject(2, "111");
ps1.execute();
System.out.println("插入一个用户1");
Thread.sleep(6000);
ps2 = conn.prepareStatement("insert into t_user (username, pwd) values (?, ?, ?)");
ps2.setObject(1, "狗子2");
ps2.setObject(2, "111");
ps2.execute();
System.out.println("插入一个用户2");
conn.commit();
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 一定要将三个try catch分开写
if (ps1 != null) {
try {
ps1.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps2 != null) {
try {
ps2.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
时间类型
Date、Timestamp比较和插入随机日期
package com.sxt.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Random;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @desc: | 测试时间处理(java.sql.Date, java.sql.Time, java.sql.Timestamp)
*/
public class Demo07 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps1 = null;
try {
// 加载驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
for (int i = 0; i < 1000; i++) {
ps1 = conn.prepareStatement("insert into t_user (username, pwd, regTime, lastLoginTime) values (?, ?, ?, ?)");
ps1.setObject(1, "狗子" + i);
ps1.setObject(2, "111");
// 定义随机数
int rand = 10000000 + new Random().nextInt(1000000000);
java.sql.Date date = new java.sql.Date(System.currentTimeMillis() - rand);
ps1.setDate(3, date);
// 如果需要插入制定日期,可以使用Calendar或DateFormat
java.sql.Timestamp stamp = new java.sql.Timestamp(System.currentTimeMillis());
ps1.setTimestamp(4, stamp);
ps1.execute();
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
// 一定要将三个try catch分开写
if (ps1 != null) {
try {
ps1.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
取出指定日期范围的记录
package com.sxt.jdbc;
import java.sql.*;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @desc: | 测试时间处理,取出指定时间段的数据
*/
public class Demo08 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 加载驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
// 选择满足regTime条件的记录,Timestamp格式同理,把java.sql.Date改成java.sql.Timestamp即可getDate改成getTimestamp
ps = conn.prepareStatement("select * from t_user where regTime>? and regTime");
java.sql.Date start = new java.sql.Date(str2Date("2020-3-1 10:23:45"));
java.sql.Date end = new java.sql.Date(str2Date("2020-3-3 10:23:45"));
ps.setObject(1, start);
ps.setObject(2, end);
rs = ps.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("id") + "-->" + rs.getString("username") + "-->" + rs.getDate("regTime"));
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
// 一定要将三个try catch分开写
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public static long str2Date(String dateStr){
/*将字符串代表的日期转为long数字(格式:yyyy-MM-dd hh:mm:ss)*/
DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
try {
return format.parse(dateStr).getTime();
} catch (ParseException e) {
e.printStackTrace();
return 0;
}
}
}
Character Large Object
用于存储大量的文本数据
大字段有些特殊,不同数据库处理的方式不一样,大字段的操作常常是以流的方式来处理的。而非一般的字段,一次即可读出数据。
Mysql中相关类型:
测试CLOB文本大对象的使用
包含:将字符串、文件内容插入数据库中的CLOB字段,将CLOB字段值取出来操作
package com.sxt.jdbc;
import java.io.*;
import java.sql.*;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @desc: | 测试CLOB文本大对象的使用
* 包含:将字符串、文件内容插入数据库中的CLOB字段,将CLOB字段值取出来操作
*/
public class Demo09 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 加载驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
ps = conn.prepareStatement("insert into t_user2 (username, myInfo) values (?, ?)");
ps.setString(1, "狗子");
// 将文本文件的内容直接输入到数据库中
// ps.setClob(2, new FileReader(new File("a1.txt")));
// 通过流的操作写入字符串内容
// ps.setClob(2, new BufferedReader(new InputStreamReader(new ByteArrayInputStream("aaaabbbb".getBytes()))));
// ps.executeUpdate();
// 读取Clob字段
ps = conn.prepareStatement("select * from t_user2 where username=?");
ps.setObject(1, "狗子");
rs = ps.executeQuery();
while (rs.next()) {
Clob c = rs.getClob("myInfo");
Reader r = c.getCharacterStream();
int temp = 0;
while((temp = r.read()) != -1){
System.out.print((char)temp);
}
System.out.println();
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 一定要将三个try catch分开写
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
Binary Large Object
用于存储大量的二进制数据
大字段有些特殊,不同数据库处理的方式不一样,大字段的操作常常是以流的方式来处理的。而非一般的字段,一次即可独处数据。
Mysql中相关类型与CLOB类似,只是将CLOB改为BLOB
测试BLOB二进制大对象的使用
package com.sxt.jdbc;
import java.io.*;
import java.sql.*;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @desc: | 测试BLOB二进制大对象的使用
*/
public class Demo10 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 加载驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
// ps = conn.prepareStatement("insert into t_user2 (username, headImg) values (?, ?)");
// ps.setString(1, "狗子2");
// 将图片文件的内容直接输入到数据库中
// ps.setBlob(2, new FileInputStream("test.png"));
// ps.executeUpdate();
// 读取Blob字段
ps = conn.prepareStatement("select * from t_user2 where username=?");
ps.setObject(1, "狗子2");
rs = ps.executeQuery();
while (rs.next()) {
Blob b = rs.getBlob("headImg");
InputStream is = b.getBinaryStream();
OutputStream os = new FileOutputStream("a1_input.png");
int temp = 0;
while((temp = is.read()) != -1){
os.write(temp);
}
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 一定要将三个try catch分开写
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
JDBC工具类
包括返回数据库驱动连接,关闭各个接口
package com.sxt.jdbc;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: JDBCUTIL.java
* @time: 2020/3/8 19:43
* @desc: |JDBC工具类
*/
public class JDBCUtil {
// 可以帮助我们读取和处理资源文件中的信息
private static Properties pros = null;
static {
/*静态代码块:只有在加载JDBCUtil类的时候调用一次*/
pros = new Properties();
try {
pros.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/sxt/jdbc/db.properties"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static Connection getMysqlConn() {
/*获取数据库(mysql)驱动连接*/
// 加载驱动类
try {
Class.forName(pros.getProperty("mysqlDriver"));
return DriverManager.getConnection(
pros.getProperty("mysqlURL"),
pros.getProperty("mysqlUser"),
pros.getProperty("mysqlPwd"));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static Connection getOracleConn() {
/*获取数据库(oracle)驱动连接*/
// 加载驱动类
try {
Class.forName(pros.getProperty("oracleDriver"));
return DriverManager.getConnection(
pros.getProperty("oracleURL"),
pros.getProperty("oracleUser"),
pros.getProperty("oraclePwd"));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void close(ResultSet rs, Statement ps, Connection conn){
/*关闭接口方法*/
try {
if (rs != null){
rs.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (ps != null){
ps.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (conn != null){
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void close(Statement ps, Connection conn){
/*关闭接口方法,重载*/
try {
if (ps != null){
ps.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (conn != null){
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void close(Connection conn){
/*关闭接口方法,重载*/
try {
if (conn != null){
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
配置文件封装
mysqlURL=jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC
#mysqlURL=jdbc:mysql://localhost:3306/sorm?serverTimezone=UTC
mysqlDriver=com.mysql.cj.jdbc.Driver
mysqlUser=root
mysqlPwd=123456
oracleDriver=oracle.jdbc.driver.OracleDriver
oracleURL=jdbc:oracle:thin:@localhost:1521:database
oracleUser=scott
oraclePwd=tiger
测试使用JDBCUtil工具类来简化JDBC开发
package com.sxt.jdbc;
import java.sql.*;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @desc: | 测试使用JDBCUtil工具类来简化JDBC开发
*/
public class Demo11 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtil.getMysqlConn();
ps = conn.prepareStatement("insert into t_user (username) values (?)");
ps.setString(1, "hehe");
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(rs, ps, conn);
}
}
}
ORM的基本思想
将表中的一条记录封装到Object数组中
将表中的一条记录封装到map中
将表中的一条记录封装到javabean对象中
测试使用Object数组来封装一条记录,使用List
package com.sxt.testORM;
import com.sxt.jdbc.JDBCUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: Demo01.java
* @time: 2020/3/10 13:29
* @desc: |测试使用Object数组来封装一条记录
* 使用List
public class Demo01 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
List<Object[]> list = new ArrayList<>();
try {
conn = JDBCUtil.getMysqlConn();
ps = conn.prepareStatement("select empname, salary, age from emp where id > ?");
ps.setObject(1, 0);
rs = ps.executeQuery();
while (rs.next()) {
// System.out.println(rs.getString(1) + "-->" + rs.getDouble(2) + "-->" + rs.getInt(3));
Object[] objs = new Object[3];
objs[0] = rs.getObject(1);
objs[1] = rs.getObject(2);
objs[2] = rs.getObject(3);
list.add(objs);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtil.close(rs, ps, conn);
}
for (Object[] objs : list) {
System.out.println(objs[0] + "-->" + objs[1] + "-->" + objs[2]);
}
}
}
测试使用Map来封装一条记录,使用List
存储多条记录(也可用Map
)
package com.sxt.testORM;
import com.sxt.jdbc.JDBCUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @desc: |测试使用Map来封装一条记录
* 使用List
public class Demo02 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
// 使用一个Map封装一条记录
List<Map<String, Object>> list = new ArrayList<>();
try {
conn = JDBCUtil.getMysqlConn();
ps = conn.prepareStatement("select empname, salary, age from emp where id > ?");
ps.setObject(1, 0);
rs = ps.executeQuery();
while (rs.next()) {
// System.out.println(rs.getString(1) + "-->" + rs.getDouble(2) + "-->" + rs.getInt(3));
Map<String, Object> row = new HashMap<>();
row.put("empname", rs.getString(1));
row.put("salary", rs.getString(2));
row.put("age", rs.getString(3));
list.add(row);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtil.close(rs, ps, conn);
}
// 遍历List和Map
for (Map<String, Object> row : list) {
for (String key : row.keySet()) {
System.out.print(key + "-->" + row.get(key) + "\t\t");
}
System.out.println();
}
}
}
使用Javabean对象来封装一条记录,使用List
存储多条记录
package com.sxt.testORM;
import com.sxt.jdbc.JDBCUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @desc: |使用Javabean对象来封装一条记录
* 使用List存储多条记录
*/
public class Demo03 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
List<Emp> list = new ArrayList<>();
try {
conn = JDBCUtil.getMysqlConn();
ps = conn.prepareStatement("select empname, salary, age from emp where id > ?");
ps.setObject(1, 0);
rs = ps.executeQuery();
while (rs.next()) {
// System.out.println(rs.getString(1) + "-->" + rs.getDouble(2) + "-->" + rs.getInt(3));
Emp emp = new Emp(rs.getString(1), rs.getInt(2), rs.getDouble(3));
list.add(emp);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtil.close(rs, ps, conn);
}
for (Emp e: list) {
System.out.println(e);
}
}
}
其中需要为每一个表定义相同结构的类
Emp
package com.sxt.testORM;
import java.sql.Date;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: Emp.java
* @time: 2020/3/10 14:35
* @desc: |表结构和类对应
*/
public class Emp {
private Integer id;
private String empname;
private Integer age;
private Double salary;
private Date birthday;
private Integer deptId;
public Emp(String empname, Integer age, Double salary) {
this.empname = empname;
this.age = age;
this.salary = salary;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getEmpname() {
return empname;
}
public void setEmpname(String empname) {
this.empname = empname;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
public Emp(String empname, Integer age, Double salary, Date birthday, Integer deptId) {
this.empname = empname;
this.age = age;
this.salary = salary;
this.birthday = birthday;
this.deptId = deptId;
}
public Emp(Integer id, String empname, Integer age, Double salary, Date birthday, Integer deptId) {
this.id = id;
this.empname = empname;
this.age = age;
this.salary = salary;
this.birthday = birthday;
this.deptId = deptId;
}
public Emp() {
}
@Override
public String toString() {
return empname + "-->" + age + "-->" + salary;
}
}
Dept
package com.sxt.testORM;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: Dept.java
* @time: 2020/3/10 14:38
* @desc: |
*/
public class Dept {
private Integer id;
private String dname;
private String address;
public Dept() {
}
public Dept(String dname, String address) {
this.dname = dname;
this.address = address;
}
public Dept(Integer id, String dname, String address) {
this.id = id;
this.dname = dname;
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
Simple Object Relationship Mapping
我们希望设计一个可以实现对象和SQL自动映射的框架,但是整体用法和设计比Hibernate简单。砍掉不必要的功能。
会穿插使用设计模式
从对象到sql
从sql到对象
List
核心架构:
架构图
核心bean,封装相关数据
针对SORM框架的说明:
bean
ColumnInfo
package com.sxt.SORM.bean;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: ColumnInfo.java
* @time: 2020/3/11 13:46
* @desc: |封装表中一个字段的信息
*/
public class ColumnInfo {
// 字段名称
private String name;
// 字段数据类型
private String dataType;
// 字段的键类型(0普通键;1主键;2外键)
private int keyType;
public ColumnInfo() {
}
public ColumnInfo(String name, String dataType, int keyType) {
this.name = name;
this.dataType = dataType;
this.keyType = keyType;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDataType() {
return dataType;
}
public void setDataType(String dataType) {
this.dataType = dataType;
}
public int getKeyType() {
return keyType;
}
public void setKeyType(int keyType) {
this.keyType = keyType;
}
}
Configuration
package com.sxt.SORM.bean;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: Configuration.java
* @time: 2020/3/11 13:55
* @desc: |管理配置信息
*/
public class Configuration {
// 正在使用哪个数据库
private String usingDb;
// jdbc的url
private String URL;
// 驱动类
private String driver;
// 数据库的用户名
private String user;
// 数据库的密码
private String pwd;
// 项目的源码路径
private String srcPath;
// 扫描生成java类的包(po的意思是Persistence Object持久化对象)
private String poPackage;
public Configuration() {
}
public Configuration(String usingDb, String URL, String driver, String user, String pwd, String srcPath, String poPackage) {
this.usingDb = usingDb;
this.URL = URL;
this.driver = driver;
this.user = user;
this.pwd = pwd;
this.srcPath = srcPath;
this.poPackage = poPackage;
}
public String getUsingDb() {
return usingDb;
}
public void setUsingDb(String usingDb) {
this.usingDb = usingDb;
}
public String getURL() {
return URL;
}
public void setURL(String URL) {
this.URL = URL;
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getSrcPath() {
return srcPath;
}
public void setSrcPath(String srcPath) {
this.srcPath = srcPath;
}
public String getPoPackage() {
return poPackage;
}
public void setPoPackage(String poPackage) {
this.poPackage = poPackage;
}
}
JavaFieldGetSet
package com.sxt.SORM.bean;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: JavaFieldGetSet.java
* @time: 2020/3/11 18:24
* @desc: |封装了java属性和get、set方法的源代码
*/
public class JavaFieldGetSet {
// 属性源码信息。如:private int userId;
private String fieldInfo;
// get方法的源码信息。如:public int getUserId;
private String getInfo;
// set方法的源码信息。如:public void setUserId(int id){this.id = id;}
private String setInfo;
public JavaFieldGetSet() {
}
public JavaFieldGetSet(String fieldInfo, String getInfo, String setInfo) {
this.fieldInfo = fieldInfo;
this.getInfo = getInfo;
this.setInfo = setInfo;
}
public String getFieldInfo() {
return fieldInfo;
}
public void setFieldInfo(String fieldInfo) {
this.fieldInfo = fieldInfo;
}
public String getGetInfo() {
return getInfo;
}
public void setGetInfo(String getInfo) {
this.getInfo = getInfo;
}
public String getSetInfo() {
return setInfo;
}
public void setSetInfo(String setInfo) {
this.setInfo = setInfo;
}
@Override
public String toString() {
// System.out.println(fieldInfo);
// System.out.println(getInfo);
// System.out.println(setInfo);
return super.toString();
}
}
TableInfo
package com.sxt.SORM.bean;
import java.util.List;
import java.util.Map;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: TableInfo.java
* @time: 2020/3/11 13:56
* @desc: |存储表结构信息
*/
public class TableInfo {
// 表名
private String tname;
// 所有字段的信息
private Map<String, ColumnInfo> columns;
// 唯一主键(目前只能处理表中有且只有一个的情况)
private ColumnInfo onlyPriKey;
// 如果联合主键,则在这里存储
private List<ColumnInfo> priKeys;
public TableInfo() {
}
public TableInfo(String tname, List<ColumnInfo> priKeys, Map<String, ColumnInfo> columns) {
this.tname = tname;
this.columns = columns;
this.priKeys = priKeys;
}
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
public Map<String, ColumnInfo> getColumns() {
return columns;
}
public void setColumns(Map<String, ColumnInfo> columns) {
this.columns = columns;
}
public ColumnInfo getOnlyPriKey() {
return onlyPriKey;
}
public void setOnlyPriKey(ColumnInfo onlyPriKey) {
this.onlyPriKey = onlyPriKey;
}
public List<ColumnInfo> getPriKeys() {
return priKeys;
}
public void setPriKeys(List<ColumnInfo> priKeys) {
this.priKeys = priKeys;
}
}
core
Query
package com.sxt.SORM.core;
import java.util.List;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: Query.java
* @time: 2020/3/10 17:31
* @desc: |负责查询(对外提供服务的核心类)
*/
public interface Query {
/**
* 直接执行一个DML语句
*
* @param sql sql语句
* @param params 参数
* @return 执行sql语句后影响记录的行数
*/
public int executeDML(String sql, Object[] params);
/**
* 将一个对象存储到数据库中
*
* @param obj 要存储的对象
*/
public void insert(Object obj);
/**
* 删除clazz表示类对应的表中的记录(指定主键id的记录)
* 把对象中不为null的属性往数据库中存储!如果数字为null则放0
* @param clazz 跟表对应的类的Class对象
* @param id 主键的值
*/
// delete from User where id = 2;
public void delete(Class clazz, Object id);
/**
* 删除对象在数据库中对应的记录(对象所在类对应到表,对象的主键对应到的记录)
*
* @param obj
*/
public void delete(Object obj);
/**
* 更新对象对应的记录,并且只更新指定的字段的值
*
* @param obj 索要更新的对象
* @param fieldNames 更新的属性列表
* @return 执行sql语句后影响记录的行数
*/
// update user set uname=?, pwe=?
public int update(Object obj, String[] fieldNames);
/**
* 查询返回多行记录,并将每行记录封装到clazz指定的类的对象中
*
* @param sql 查询语句
* @param clazz 封装数据的javabean类的Class对象
* @param params sql的参数
* @return 返回查询到的结果
*/
public List queryRows(String sql, Class clazz, Object[] params);
/**
* 查询返回一行记录,并将该记录封装到clazz指定的类的对象中
*
* @param sql 查询语句
* @param clazz 封装数据的javabean类的Class对象
* @param params sql的参数
* @return 返回查询到的结果
*/
public Object queryUniqueRows(String sql, Class clazz, Object[] params);
/**
* 查询返回一个值(一行一列),并将该值返回
*
* @param sql 查询语句
* @param params sql的参数
* @return 返回查询到的结果
*/
public Object queryValue(String sql, Object[] params);
/**
* 查询返回一个数字(一行一列),并将该值返回
*
* @param sql 查询语句
* @param params sql的参数
* @return 返回查询到的数字
*/
public Number queryNumber(String sql, Object[] params);
}
MysqlQuery
package com.sxt.SORM.core;
import com.sxt.SORM.bean.ColumnInfo;
import com.sxt.SORM.bean.TableInfo;
import com.sxt.SORM.po.Emp;
import com.sxt.SORM.utils.JDBCUtils;
import com.sxt.SORM.utils.ReflectUtils;
import com.sxt.SORM.vo.EmpVO;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: MysqlQuery.java
* @time: 2020/3/13 16:54
* @desc: |负责针对mysql数据库的查询
*/
public class MysqlQuery implements Query {
public static void main(String[] args) {
Object obj = new MysqlQuery().queryValue("select count(*) from emp where salary>?", new Object[]{1000});
System.out.println(obj);
}
/**
* 复杂多行查询测试
*/
public static void testQueryRows() {
List<Emp> list = new MysqlQuery().queryRows("select id,empname,age from emp where age>? and salary", Emp.class,
new Object[]{1, 9000});
for (Emp e : list) {
System.out.println(e.getEmpname());
}
String sql2 = "select e.id,e.empname,salary+bonus 'xinshui',age,d.dname 'deptName',d.address 'deptAddr' from emp e"
+ " " + "join dept d on e.deptId=d.id;";
List<EmpVO> list2 = new MysqlQuery().queryRows(sql2, EmpVO.class, null);
for (EmpVO e : list2) {
System.out.println(e.getEmpname() + "-" + e.getDeptAddr() + "-" + e.getXinshui());
}
}
/**
* 增删改操作测试
*/
public static void testDML() {
Emp e = new Emp();
e.setEmpname("Tom");
e.setBirthday(new java.sql.Date(System.currentTimeMillis()));
e.setAge(30);
e.setSalary(8888.0);
e.setId(1);
// new MysqlQuery().delete(e);
// new MysqlQuery().insert(e);
new MysqlQuery().update(e, new String[]{"empname", "age", "salary"});
}
@Override
public int executeDML(String sql, Object[] params) {
Connection conn = DBManager.getConn();
int count = 0;
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(sql);
// 给sql设置参数,就是?位置的参数
JDBCUtils.handleParams(ps, params);
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBManager.close(ps, conn);
}
return count;
}
@Override
public void insert(Object obj) {
// obj --> 表中。 insert into 表名(id, name, pwd) values (?, ?, ?)
Class c = obj.getClass();
// 存储sql的参数对象
List<Object> params = new ArrayList<>();
TableInfo tableInfo = TableContext.poClassTableMap.get(c);
StringBuilder sql = new StringBuilder("insert into " + tableInfo.getTname() + " (");
// 计算不为空的属性值
int countNotNullField = 0;
// 目前只能处理数据库来维护自增的方式
Field[] fs = c.getDeclaredFields();
for (Field f : fs) {
String fieldName = f.getName();
Object fieldValue = ReflectUtils.invokeGet(fieldName, obj);
if (fieldValue != null) {
// 如果该属性值不为空
countNotNullField++;
sql.append(fieldName + ",");
params.add(fieldValue);
}
}
// 把最后一个属性后面的,换成)
sql.setCharAt(sql.length() - 1, ')');
sql.append(" values (");
for (int i = 0; i < countNotNullField; i++) {
sql.append("?,");
}
sql.setCharAt(sql.length() - 1, ')');
executeDML(sql.toString(), params.toArray());
}
@Override
public void delete(Class clazz, Object id) {
// Emp.class, 2 --> delete from emp where id=2
// 通过Class对象找TableInfo
TableInfo tableInfo = TableContext.poClassTableMap.get(clazz);
// 获得主键
ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey();
String sql = "delete from " + tableInfo.getTname() + " where " + onlyPriKey.getName() + "=?;";
executeDML(sql, new Object[]{id});
}
@Override
public void delete(Object obj) {
Class c = obj.getClass();
TableInfo tableInfo = TableContext.poClassTableMap.get(c);
// 获得主键
ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey();
// 通过反射机制,调用属性对应的get方法或set方法
Object priKeyValue = ReflectUtils.invokeGet(onlyPriKey.getName(), obj);
delete(obj.getClass(), priKeyValue);
}
@Override
public int update(Object obj, String[] fieldNames) {
// obj{"uname", "pwd} --> update 表名 set uname=?, pwd=? where id=?
Class c = obj.getClass();
List<Object> params = new ArrayList<>();
TableInfo tableInfo = TableContext.poClassTableMap.get(c);
ColumnInfo priKey = tableInfo.getOnlyPriKey();
StringBuilder sql = new StringBuilder("update " + tableInfo.getTname() + " set ");
for (String fname : fieldNames) {
Object fvalue = ReflectUtils.invokeGet(fname, obj);
params.add(fvalue);
sql.append(fname + "=?,");
}
sql.setCharAt(sql.length() - 1, ' ');
sql.append(" where ");
sql.append(priKey.getName() + "=?");
params.add(ReflectUtils.invokeGet(priKey.getName(), obj));
return executeDML(sql.toString(), params.toArray());
}
@Override
public List queryRows(String sql, Class clazz, Object[] params) {
Connection conn = DBManager.getConn();
// 存放查询结果的容器
List list = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
// 给sql设置参数,就是?位置的参数
JDBCUtils.handleParams(ps, params);
rs = ps.executeQuery();
ResultSetMetaData metaData = rs.getMetaData();
// 多行
while (rs.next()) {
if (list == null) {
list = new ArrayList();
}
// 调用javabean的无参构造器
Object rowObj = clazz.newInstance();
// 多列 select username, pwd, age from user where id>? and age>?
for (int i = 0; i < metaData.getColumnCount(); i++) {
// username
String columnName = metaData.getColumnLabel(i + 1);
Object columnValue = rs.getObject(i + 1);
// 调用rowObj对象的setUsername(String uname)方法,将columnValue的值设置进去
ReflectUtils.invokeSet(rowObj, columnName, columnValue);
}
list.add(rowObj);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DBManager.close(ps, conn);
}
return list;
}
@Override
public Object queryUniqueRows(String sql, Class clazz, Object[] params) {
List list = queryRows(sql, clazz, params);
return (list == null && list.size() > 0) ? null : list.get(0);
}
@Override
public Object queryValue(String sql, Object[] params) {
Connection conn = DBManager.getConn();
// 存储查询结果的对象
Object value = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
// 给sql设置参数,就是?位置的参数
JDBCUtils.handleParams(ps, params);
rs = ps.executeQuery();
// 多行
while (rs.next()) {
// select count(*) from user
value = rs.getObject(1);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DBManager.close(ps, conn);
}
return value;
}
@Override
public Number queryNumber(String sql, Object[] params) {
return (Number) queryValue(sql, params);
}
}
TypeConvertor
package com.sxt.SORM.core;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: TypeConvertor.java
* @time: 2020/3/11 13:39
* @desc: |负责java数据类型和数据库数据类型的互相转换
*/
public interface TypeConvertor {
/**
* 将数据库数据类型转化成java的数据类型
* @param columnType 数据库字段的数据类型
* @return java的数据类型
*/
public String databaseType2JavaType(String columnType);
/**
* 将java数据类型转化为数据库数据类型
* @param javaDataType java数据类型
* @return 数据库数据类型
*/
public String javaType2DatabaseType(String javaDataType);
}
MySqlTypeConvertor
package com.sxt.SORM.core;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: MySqlTypeConvertor.java
* @time: 2020/3/11 18:16
* @desc: |mysql数据类型和java数据类型的转换
*/
public class MySqlTypeConvertor implements TypeConvertor {
@Override
public String databaseType2JavaType(String columnType) {
// varchar --> String
if ("varchar".equalsIgnoreCase(columnType) || "char".equalsIgnoreCase(columnType)) {
return "String";
} else if ("int".equalsIgnoreCase(columnType)
|| "tinyint".equalsIgnoreCase(columnType)
|| "smallint".equalsIgnoreCase(columnType)
|| "integer".equalsIgnoreCase(columnType)) {
return "Integer";
} else if ("bigint".equalsIgnoreCase(columnType)) {
return "long";
} else if ("double".equalsIgnoreCase(columnType) || "float".equalsIgnoreCase(columnType)) {
return "Double";
} else if ("clob".equalsIgnoreCase(columnType)) {
return "java.sql.Clob";
} else if ("blob".equalsIgnoreCase(columnType)) {
return "java.sql.Blob";
}else if("date".equalsIgnoreCase(columnType)){
return "java.sql.Date";
}else if("time".equalsIgnoreCase(columnType)){
return "java.sql.Time";
}else if("timestamp".equalsIgnoreCase(columnType)){
return "java.sql.Timestamp";
}
return null;
}
@Override
public String javaType2DatabaseType(String javaDataType) {
return null;
}
}
DBManager
package com.sxt.SORM.core;
import com.sxt.SORM.bean.Configuration;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: DBManager.java
* @time: 2020/3/11 13:43
* @desc: |根据配置信息,维持连接对象的管理(增加连接池功能)
*/
public class DBManager {
private static Configuration conf;
static {
// 静态代码块
Properties pros = new Properties();
try {
pros.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/sxt/SORM/db.properties"));
} catch (IOException e) {
e.printStackTrace();
}
conf = new Configuration();
conf.setDriver(pros.getProperty("driver"));
conf.setPoPackage(pros.getProperty("poPackage"));
conf.setPwd(pros.getProperty("pwd"));
conf.setSrcPath(pros.getProperty("srcPath"));
conf.setURL(pros.getProperty("URL"));
conf.setUser(pros.getProperty("user"));
conf.setUsingDb(pros.getProperty("usingDB"));
}
public static Connection getConn() {
/*获取数据库(mysql)驱动连接*/
// 加载驱动类
try {
Class.forName(conf.getDriver());
// 直接建立连接,后期增加连接池处理,提高效率!
return DriverManager.getConnection(conf.getURL(), conf.getUser(), conf.getPwd());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void close(ResultSet rs, Statement ps, Connection conn) {
/*关闭接口方法*/
try {
if (rs != null) {
rs.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (ps != null) {
ps.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void close(Statement ps, Connection conn) {
/*关闭接口方法,重载*/
try {
if (ps != null) {
ps.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void close(Connection conn) {
/*关闭接口方法,重载*/
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Configuration getConf(){
return conf;
}
}
TableContext
package com.sxt.SORM.core;
import com.sxt.SORM.bean.ColumnInfo;
import com.sxt.SORM.bean.TableInfo;
import com.sxt.SORM.utils.JavaFileUtils;
import com.sxt.SORM.utils.StringUtils;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: TableContext.java
* @time: 2020/3/11 13:42
* @desc: |负责获取管理数据库所有表结构和类结构的关系,并可以根据表结构生成类结结构
*/
public class TableContext {
// 表名为key,表信息对象为value
public static Map<String, TableInfo> tables = new HashMap<>();
// 将po的calss对象和表信息对象关联起来,便于重用。
public static Map<Class, TableInfo> poClassTableMap = new HashMap<>();
private TableContext() {
}
static {
try {
// 初始化获得的表信息
Connection conn = DBManager.getConn();
DatabaseMetaData dbmd = conn.getMetaData();
ResultSet tableSet = dbmd.getTables("", "%", "%", new String[]{"TABLE"});
while (tableSet.next()) {
// 循环每个表名
String tableName = (String) tableSet.getObject("TABLE_NAME");
TableInfo ti = new TableInfo(tableName, new ArrayList<ColumnInfo>(), new HashMap<String, ColumnInfo>());
tables.put(tableName, ti);
// 查询表中的所有字段
ResultSet set = dbmd.getColumns("", "%", tableName, "%");
while (set.next()) {
// 循环每个列名
ColumnInfo ci = new ColumnInfo(set.getString("COLUMN_NAME"), set.getString("TYPE_NAME"), 0);
ti.getColumns().put(set.getString("COLUMN_NAME"), ci);
}
// 查询表中的主键
// System.out.println(tableName);
ResultSet set2 = dbmd.getPrimaryKeys("", "%", tableName);
while (set2.next()) {
ColumnInfo ci2 = (ColumnInfo) ti.getColumns().get(set2.getObject("COLUMN_NAME"));
// 设置为主键类型
ci2.setKeyType(1);
ti.getPriKeys().add(ci2);
}
if (ti.getPriKeys().size() > 0) {
// 取唯一主键。方便使用。如果是联合主键。则为空!
ti.setOnlyPriKey(ti.getPriKeys().get(0));
}
}
} catch (Exception e) {
e.printStackTrace();
}
// 更新类结构
updateJavaPOFile();
// 加载po包下面的所有类,便于重用,提高效率!
loadPOTables();
}
/**
* 根据表结构,更新配置的po包下面的java类
*/
public static void updateJavaPOFile() {
Map<String, TableInfo> map = TableContext.tables;
for (TableInfo t : map.values()) {
JavaFileUtils.createJavaPOFile(t, new MySqlTypeConvertor());
}
}
/**
* 加载po包下面的类
*/
public static void loadPOTables() {
for (TableInfo tableInfo : tables.values()) {
try {
Class c = Class.forName(DBManager.getConf().getPoPackage() + "." + StringUtils.firstChar2UpperCase(tableInfo.getTname()));
poClassTableMap.put(c, tableInfo);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Map<String, TableInfo> tables = TableContext.tables;
System.out.println(tables);
}
}
utils
JavaFileUtils
package com.sxt.SORM.utils;
import com.sxt.SORM.bean.ColumnInfo;
import com.sxt.SORM.bean.JavaFieldGetSet;
import com.sxt.SORM.bean.TableInfo;
import com.sxt.SORM.core.DBManager;
import com.sxt.SORM.core.MySqlTypeConvertor;
import com.sxt.SORM.core.TableContext;
import com.sxt.SORM.core.TypeConvertor;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: JavaFileUtils.java
* @time: 2020/3/11 13:44
* @desc: | 封装了生成java文件(源代码)常用操作
*/
public class JavaFileUtils {
/**
* 根据字段信息生成java属性信息。如varchar username --> private String username;以及相应的set和get方法源码
*
* @param column 字段信息
* @param convertor 类型转化器
* @return java属性和set/get方法源码
*/
public static JavaFieldGetSet createFieldGetSetSRC(ColumnInfo column, TypeConvertor convertor) {
JavaFieldGetSet jfgs = new JavaFieldGetSet();
String javaFieldType = convertor.databaseType2JavaType(column.getDataType());
jfgs.setFieldInfo("\tprivate " + javaFieldType + " " + column.getName() + ";\n");
// public String getUsername(){return username;}
StringBuilder getSrc = new StringBuilder();
getSrc.append("\tpublic " + javaFieldType + " get" + StringUtils.firstChar2UpperCase(column.getName()) + "(){\n");
getSrc.append("\t\treturn " + column.getName() + ";\n");
getSrc.append("\t}\n");
jfgs.setGetInfo(getSrc.toString());
// public void setUsername(String username){this.username = username;}
StringBuilder setSrc = new StringBuilder();
setSrc.append("\tpublic void set" + StringUtils.firstChar2UpperCase(column.getName()) + "(");
setSrc.append(javaFieldType + " " + column.getName() + "){\n");
setSrc.append("\t\tthis." + column.getName() + " = " + column.getName() + ";\n");
setSrc.append("\t}\n");
jfgs.setSetInfo(setSrc.toString());
return jfgs;
}
/**
* 根据表信息生成java类的源代码
*
* @param tableInfo 表信息
* @param convertor 数据类型转化器
* @return java类的源代码
*/
public static String createJavaSrc(TableInfo tableInfo, TypeConvertor convertor) {
Map<String, ColumnInfo> columns = tableInfo.getColumns();
List<JavaFieldGetSet> javaFields = new ArrayList<>();
for (ColumnInfo c : columns.values()) {
javaFields.add(createFieldGetSetSRC(c, convertor));
}
StringBuilder src = new StringBuilder();
// 生成package语句
src.append("package " + DBManager.getConf().getPoPackage() + ";\n\n");
// 生成import语句
src.append("import java.sql.*;\n");
src.append("import java.util.*;\n\n");
// 生成类声明语句
src.append("public class " + StringUtils.firstChar2UpperCase(tableInfo.getTname()) + " {\n\n");
// 生成属性列表
for (JavaFieldGetSet f : javaFields) {
src.append(f.getFieldInfo());
}
src.append("\n\n");
// 生成set方法列表
for (JavaFieldGetSet f : javaFields) {
src.append(f.getSetInfo());
}
// 生成get方法列表
for (JavaFieldGetSet f : javaFields) {
src.append(f.getGetInfo());
}
// 生成类结束
src.append("}\n");
// System.out.println(src);
return src.toString();
}
public static void createJavaPOFile(TableInfo tableInfo, TypeConvertor convertor) {
String src = createJavaSrc(tableInfo, convertor);
String srcPath = DBManager.getConf().getSrcPath() + "\\";
String packagePath = DBManager.getConf().getPoPackage().replaceAll("\\.", "/");
// 修正poPackage路径,因为没有重新创建项目
String[] packagePath_list = packagePath.split("/");
packagePath = packagePath_list[packagePath_list.length - 1];
File f = new File(srcPath + packagePath);
// System.out.println(f.getAbsolutePath());
if (!f.exists()) {
// 指定目录不存在则帮助用户建立该目录
f.mkdirs();
}
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter(f.getAbsolutePath() + "/" + StringUtils.firstChar2UpperCase(tableInfo.getTname()) + ".java"));
bw.write(src);
System.out.println("建立表" + tableInfo.getTname() + "对应的java类");
bw.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (bw != null) {
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// 测试每一个表的field,set、get方法源码生成
// ColumnInfo ci = new ColumnInfo("username", "int", 0);
// JavaFieldGetSet f = createFieldGetSetSRC(ci, new MySqlTypeConvertor());
// System.out.println(f);
// System.out.println("\n--------------------" + "分割线" + "--------------------\n");
// 测试每一个表的从头到尾完全源码生成
// Map map = TableContext.tables;
// TableInfo t = map.get("emp");
// createJavaSrc(t, new MySqlTypeConvertor());
Map<String, TableInfo> map = TableContext.tables;
// TableInfo t = map.get("emp");
for(TableInfo t: map.values()) {
createJavaPOFile(t, new MySqlTypeConvertor());
}
}
}
JDBCUtils
package com.sxt.SORM.utils;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: JDBCUtils.java
* @time: 2020/3/11 13:43
* @desc: | 封装了JDBC查询常用的操作
*/
public class JDBCUtils {
/**
* 给sql设置参数,就是?位置的参数
* @param ps 预编译sql语句对象
* @param params 参数
*/
public static void handleParams(PreparedStatement ps, Object[] params) {
if (params != null) {
for (int i = 0; i < params.length; i++) {
try {
ps.setObject(1 + i, params[i]);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
StringUtils
package com.sxt.SORM.utils;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: StringUtils.java
* @time: 2020/3/11 13:44
* @desc: | 封装了字符串常用的操作
*/
public class StringUtils {
/**
* 将目标字符串首字母变为大写
* @param str 目标字符串
* @return 首字母变为大写的字符串
*/
public static String firstChar2UpperCase(String str){
// abcd-->Abcd
return str.toUpperCase().substring(0, 1) + str.substring(1);
}
}
ReflectUtils
package com.sxt.SORM.utils;
import java.lang.reflect.Method;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: ReflectUtils.java
* @time: 2020/3/11 13:44
* @desc: | 封装了反射的常用操作
*/
public class ReflectUtils {
/**
* 调用obj对象对应属性fieldName的get方法
*
* @param fieldName 属性名
* @param obj Object对象
* @return
*/
public static Object invokeGet(String fieldName, Object obj) {
// 通过反射机制,调用属性对应的get方法或set方法
try {
Class c = obj.getClass();
Method m = c.getDeclaredMethod("get" + StringUtils.firstChar2UpperCase(fieldName), null);
return m.invoke(obj, null);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void invokeSet(Object obj, String columnName, Object columnValue) {
try {
if (columnValue != null) {
Method m = obj.getClass().getDeclaredMethod("set" + StringUtils.firstChar2UpperCase(columnName),
columnValue.getClass());
m.invoke(obj, columnValue);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
po(自动生成的代码保存位置)
Dept
package com.sxt.SORM.po;
public class Dept {
private String address;
private Integer id;
private String dname;
public void setAddress(String address){
this.address = address;
}
public void setId(Integer id){
this.id = id;
}
public void setDname(String dname){
this.dname = dname;
}
public String getAddress(){
return address;
}
public Integer getId(){
return id;
}
public String getDname(){
return dname;
}
}
Emp
package com.sxt.SORM.po;
public class Emp {
private String empname;
private java.sql.Date birthday;
private Double bonus;
private Integer deptId;
private Integer id;
private Double salary;
private Integer age;
public String getEmpname() {
return empname;
}
public void setEmpname(String empname) {
this.empname = empname;
}
public java.sql.Date getBirthday() {
return birthday;
}
public void setBirthday(java.sql.Date birthday) {
this.birthday = birthday;
}
public Double getBonus() {
return bonus;
}
public void setBonus(Double bonus) {
this.bonus = bonus;
}
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
vo(复杂查询生成的类所保存的位置)
EmpVO
package com.sxt.SORM.vo;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: EmpVO.java
* @time: 2020/3/15 12:44
* @desc: |
*/
public class EmpVO {
// select e.id,e.empname,salary+bonus 'xinshui',age,d.dname 'deptName',d.address 'deptAddr' from emp e
// join dept d on e.deptId=d.id;
private Integer id;
private String empname;
private Double xinshui;
private Integer age;
private String deptName;
private String deptAddr;
public EmpVO() {
}
public EmpVO(Integer id, String empname, Double xinshui, Integer age, String deptName, String deptAddr) {
this.id = id;
this.empname = empname;
this.xinshui = xinshui;
this.age = age;
this.deptName = deptName;
this.deptAddr = deptAddr;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getEmpname() {
return empname;
}
public void setEmpname(String empname) {
this.empname = empname;
}
public Double getXinshui() {
return xinshui;
}
public void setXinshui(Double xinshui) {
this.xinshui = xinshui;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public String getDeptAddr() {
return deptAddr;
}
public void setDeptAddr(String deptAddr) {
this.deptAddr = deptAddr;
}
}
配置文件(db.properties)
usingDB=mysql
URL=jdbc:mysql://localhost:3306/sorm?serverTimezone=UTC
driver=com.mysql.cj.jdbc.Driver
user=root
pwd=123456
srcPath=F:/BookStudy/else/JAVAPro/src/com/sxt/SORM
poPackage=com.sxt.SORM.po
README(说明文件)
1. 在src下建立db.properties
2. 每张表只有一个主键,不能处理多个主键的情况
3. po尽量使用包装类,不要使用基本数据类型
4. 目前只能处理数据库来维护自增的方式
Query
package com.sxt.SORM.core;
import com.sxt.SORM.bean.ColumnInfo;
import com.sxt.SORM.bean.TableInfo;
import com.sxt.SORM.utils.JDBCUtils;
import com.sxt.SORM.utils.ReflectUtils;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: Query.java
* @time: 2020/3/10 17:31
* @desc: |负责查询(对外提供服务的核心类)
*/
public abstract class Query implements Cloneable {
/**
* 采用模板方法模式将JDBC操作封装成模板,变于重用
*
* @param sql sql语句
* @param params sql的参数
* @param clazz 记录要封装到的java类
* @param back CallBack的实现类,实现回调
* @return 返回查询结果
*/
public Object executeQueryTemplate(String sql, Object[] params, Class clazz, CallBack back) {
Connection conn = DBManager.getConn();
// 存放查询结果的容器
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
// 给sql设置参数,就是?位置的参数
JDBCUtils.handleParams(ps, params);
rs = ps.executeQuery();
ResultSetMetaData metaData = rs.getMetaData();
return back.doExecute(conn, ps, rs);
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
DBManager.close(ps, conn);
}
}
/**
* 直接执行一个DML语句
*
* @param sql sql语句
* @param params 参数
* @return 执行sql语句后影响记录的行数
*/
public int executeDML(String sql, Object[] params) {
Connection conn = DBManager.getConn();
int count = 0;
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(sql);
// 给sql设置参数,就是?位置的参数
JDBCUtils.handleParams(ps, params);
count = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBManager.close(ps, conn);
}
return count;
}
/**
* 将一个对象存储到数据库中
*
* @param obj 要存储的对象
*/
public void insert(Object obj) {
// obj --> 表中。 insert into 表名(id, name, pwd) values (?, ?, ?)
Class c = obj.getClass();
// 存储sql的参数对象
List<Object> params = new ArrayList<>();
TableInfo tableInfo = TableContext.poClassTableMap.get(c);
StringBuilder sql = new StringBuilder("insert into " + tableInfo.getTname() + " (");
// 计算不为空的属性值
int countNotNullField = 0;
// 目前只能处理数据库来维护自增的方式
Field[] fs = c.getDeclaredFields();
for (Field f : fs) {
String fieldName = f.getName();
Object fieldValue = ReflectUtils.invokeGet(fieldName, obj);
if (fieldValue != null) {
// 如果该属性值不为空
countNotNullField++;
sql.append(fieldName + ",");
params.add(fieldValue);
}
}
// 把最后一个属性后面的,换成)
sql.setCharAt(sql.length() - 1, ')');
sql.append(" values (");
for (int i = 0; i < countNotNullField; i++) {
sql.append("?,");
}
sql.setCharAt(sql.length() - 1, ')');
executeDML(sql.toString(), params.toArray());
}
/**
* 删除clazz表示类对应的表中的记录(指定主键id的记录)
* 把对象中不为null的属性往数据库中存储!如果数字为null则放0
*
* @param clazz 跟表对应的类的Class对象
* @param id 主键的值
*/
// delete from User where id = 2;
public void delete(Class clazz, Object id) {
// Emp.class, 2 --> delete from emp where id=2
// 通过Class对象找TableInfo
TableInfo tableInfo = TableContext.poClassTableMap.get(clazz);
// 获得主键
ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey();
String sql = "delete from " + tableInfo.getTname() + " where " + onlyPriKey.getName() + "=?;";
executeDML(sql, new Object[]{id});
}
/**
* 删除对象在数据库中对应的记录(对象所在类对应到表,对象的主键对应到的记录)
*
* @param obj
*/
public void delete(Object obj) {
Class c = obj.getClass();
TableInfo tableInfo = TableContext.poClassTableMap.get(c);
// 获得主键
ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey();
// 通过反射机制,调用属性对应的get方法或set方法
Object priKeyValue = ReflectUtils.invokeGet(onlyPriKey.getName(), obj);
delete(obj.getClass(), priKeyValue);
}
/**
* 更新对象对应的记录,并且只更新指定的字段的值
*
* @param obj 索要更新的对象
* @param fieldNames 更新的属性列表
* @return 执行sql语句后影响记录的行数
*/
// update user set uname=?, pwe=?
public int update(Object obj, String[] fieldNames) {
// obj{"uname", "pwd} --> update 表名 set uname=?, pwd=? where id=?
Class c = obj.getClass();
List<Object> params = new ArrayList<>();
TableInfo tableInfo = TableContext.poClassTableMap.get(c);
ColumnInfo priKey = tableInfo.getOnlyPriKey();
StringBuilder sql = new StringBuilder("update " + tableInfo.getTname() + " set ");
for (String fname : fieldNames) {
Object fvalue = ReflectUtils.invokeGet(fname, obj);
params.add(fvalue);
sql.append(fname + "=?,");
}
sql.setCharAt(sql.length() - 1, ' ');
sql.append(" where ");
sql.append(priKey.getName() + "=?");
params.add(ReflectUtils.invokeGet(priKey.getName(), obj));
return executeDML(sql.toString(), params.toArray());
}
/**
* 查询返回多行记录,并将每行记录封装到clazz指定的类的对象中
*
* @param sql 查询语句
* @param clazz 封装数据的javabean类的Class对象
* @param params sql的参数
* @return 返回查询到的结果
*/
public List queryRows(final String sql, final Class clazz, final Object[] params) {
// 存放查询结果的容器
return (List) executeQueryTemplate(sql, params, clazz, new CallBack() {
@Override
public Object doExecute(Connection conn, PreparedStatement ps, ResultSet rs) {
List list = null;
try {
ResultSetMetaData metaData = rs.getMetaData();
// 多行
while (rs.next()) {
if (list == null) {
list = new ArrayList();
}
// 调用javabean的无参构造器
Object rowObj = clazz.newInstance();
// 多列 select username, pwd, age from user where id>? and age>?
for (int i = 0; i < metaData.getColumnCount(); i++) {
// username
String columnName = metaData.getColumnLabel(i + 1);
Object columnValue = rs.getObject(i + 1);
// 调用rowObj对象的setUsername(String uname)方法,将columnValue的值设置进去
ReflectUtils.invokeSet(rowObj, columnName, columnValue);
}
list.add(rowObj);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
});
}
/**
* 查询返回一行记录,并将该记录封装到clazz指定的类的对象中
*
* @param sql 查询语句
* @param clazz 封装数据的javabean类的Class对象
* @param params sql的参数
* @return 返回查询到的结果
*/
public Object queryUniqueRows(String sql, Class clazz, Object[] params) {
List list = queryRows(sql, clazz, params);
return (list != null || list.size() > 0) ? list.get(0) : null;
}
/**
* 根据主键的值直接查找对应的对象
* @param clazz
* @param id
* @return
*/
public Object queryById(Class clazz, Object id){
// select * from emp where id=?
TableInfo tableInfo = TableContext.poClassTableMap.get(clazz);
ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey();
String sql = "select * from " + tableInfo.getTname() + " where " + onlyPriKey.getName() + "=?";
return queryUniqueRows(sql, clazz, new Object[]{id});
}
/**
* 查询返回一个值(一行一列),并将该值返回
*
* @param sql 查询语句
* @param params sql的参数
* @return 返回查询到的结果
*/
public Object queryValue(String sql, Object[] params) {
return executeQueryTemplate(sql, params, null, new CallBack() {
@Override
public Object doExecute(Connection conn, PreparedStatement ps, ResultSet rs) {
Object value = null;
try {
// 多行
while (rs.next()) {
// select count(*) from user
value = rs.getObject(1);
}
} catch (Exception e) {
e.printStackTrace();
}
return value;
}
});
}
/**
* 查询返回一个数字(一行一列),并将该值返回
*
* @param sql 查询语句
* @param params sql的参数
* @return 返回查询到的数字
*/
public Number queryNumber(String sql, Object[] params) {
return (Number) queryValue(sql, params);
}
/**
* 分页查询
*
* @param pageNum 第几页数据
* @param size 每页显示多少记录
* @return
*/
public abstract Object queryPagenate(int pageNum, int size);
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
MysqlQuery
package com.sxt.SORM.core;
import com.sxt.SORM.po.Emp;
import com.sxt.SORM.vo.EmpVO;
import java.util.List;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: MysqlQuery.java
* @time: 2020/3/13 16:54
* @desc: |负责针对mysql数据库的查询
*/
public class MysqlQuery extends Query {
public static void main(String[] args) {
Object obj = new MysqlQuery().queryValue("select count(*) from emp where salary>?", new Object[]{1000});
System.out.println(obj);
}
/**
* 复杂多行查询测试
*/
public static void testQueryRows() {
List<Emp> list = new MysqlQuery().queryRows("select id,empname,age from emp where age>? and salary", Emp.class,
new Object[]{1, 9000});
for (Emp e : list) {
System.out.println(e.getEmpname());
}
String sql2 = "select e.id,e.empname,salary+bonus 'xinshui',age,d.dname 'deptName',d.address 'deptAddr' from emp e"
+ " " + "join dept d on e.deptId=d.id;";
List<EmpVO> list2 = new MysqlQuery().queryRows(sql2, EmpVO.class, null);
for (EmpVO e : list2) {
System.out.println(e.getEmpname() + "-" + e.getDeptAddr() + "-" + e.getXinshui());
}
}
/**
* 增删改操作测试
*/
public static void testDML() {
Emp e = new Emp();
e.setEmpname("Tom");
e.setBirthday(new java.sql.Date(System.currentTimeMillis()));
e.setAge(30);
e.setSalary(8888.0);
e.setId(1);
// new MysqlQuery().delete(e);
// new MysqlQuery().insert(e);
new MysqlQuery().update(e, new String[]{"empname", "age", "salary"});
}
@Override
public Object queryPagenate(int pageNum, int size) {
return null;
}
}
修改db.properties,增加queryClass的路径
queryClass=com.sxt.SORM.core.MysqlQuery
修改Configuration,增加queryClass的属性,getset方法
// 项目使用的查询类的路径
private String queryClass;
public String getQueryClass() {
return queryClass;
}
public void setQueryClass(String queryClass) {
this.queryClass = queryClass;
}
Query实现implements Cloneable接口
public abstract class Query implements Cloneable
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
QueryFactory
package com.sxt.SORM.core;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: QueryFactory.java
* @time: 2020/3/11 13:33
* @desc: |创建Query对象的工厂类:单例+克隆+工厂
*/
public class QueryFactory {
private static QueryFactory factory = new QueryFactory();
// 原型对象
private static Query prototypeObj;
static{
try {
// 加载指定的Query类
Class c = Class.forName(DBManager.getConf().getQueryClass());
prototypeObj = (Query) c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
// 私有构造器
private QueryFactory(){}
public static Query createQuery(){
try {
return (Query) prototypeObj.clone();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
客户端调用测试类
package com.sxt.SORM.test;
import com.sxt.SORM.core.MysqlQuery;
import com.sxt.SORM.core.Query;
import com.sxt.SORM.core.QueryFactory;
import com.sxt.SORM.vo.EmpVO;
import java.util.List;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: Test.java
* @time: 2020/3/17 12:55
* @desc: |客户端调用测试类
*/
public class Test {
public static void main(String[] args){
Query q = QueryFactory.createQuery();
String sql2 = "select e.id,e.empname,salary+bonus 'xinshui',age,d.dname 'deptName',d.address 'deptAddr' from emp e"
+ " " + "join dept d on e.deptId=d.id;";
List<EmpVO> list2 = q.queryRows(sql2, EmpVO.class, null);
for (EmpVO e : list2) {
System.out.println(e.getEmpname() + "-" + e.getDeptAddr() + "-" + e.getXinshui());
}
}
}
Connection Pool
就是将Connection对象放入List中,反复重用!
连接池的初始化:事先放入多个连接对象
从连接池中取连接对象
关闭连接
不是真正关闭连接,而是将用完的连接放入连接池中。
市面上的连接池产品:
代码
配置文件新增连接池参数
poolMinSize=10
poolMaxSize=100
Configuration增加上述参数的属性和getset方法
// 连接池最小限制
private int poolMinSize;
// 连接池最大限制
private int poolMaxSize;
public int getPoolMinSize() {
return poolMinSize;
}
public void setPoolMinSize(int poolMinSize) {
this.poolMinSize = poolMinSize;
}
public int getPoolMaxSize() {
return poolMaxSize;
}
public void setPoolMaxSize(int poolMaxSize) {
this.poolMaxSize = poolMaxSize;
}
新增连接池类:DBConnPool
package com.sxt.SORM.pool;
import com.sxt.SORM.core.DBManager;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: DBConnPool.java
* @time: 2020/3/17 14:03
* @desc: |连接池的类
*/
public class DBConnPool {
// 最大连接数
private static final int POOL_MAX_SIZE = DBManager.getConf().getPoolMaxSize();
// 最小连接数
private static final int POOL_MIN_SIZE = DBManager.getConf().getPoolMinSize();
// 连接池对象
private List<Connection> pool;
public DBConnPool() {
initPool();
}
/**
* 初始化连接池,使池中的连接数达到最小值
*/
public void initPool() {
if (pool == null) {
pool = new ArrayList<Connection>();
}
while (pool.size() < DBConnPool.POOL_MIN_SIZE) {
pool.add(DBManager.createConn());
System.out.println("初始化池,池中连接数:" + pool.size());
}
}
/**
* 从连接池中取出一个连接
*/
public synchronized Connection getConnection() {
int last_index = pool.size() - 1;
// 获得一个连接
Connection conn = pool.get(last_index);
pool.remove(last_index);
return conn;
}
/**
* 将连接放回池中
*
* @param conn
*/
public synchronized void close(Connection conn) {
if (pool.size() >= POOL_MAX_SIZE) {
try {
if (conn != null) {
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
pool.add(conn);
}
}
}
修改DBManager方法,把原来获取连接的方式,改为使用连接池,关闭连接的方式也做同样的修改
package com.sxt.SORM.core;
import com.sxt.SORM.bean.Configuration;
import com.sxt.SORM.pool.DBConnPool;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: DBManager.java
* @time: 2020/3/11 13:43
* @desc: |根据配置信息,维持连接对象的管理(增加连接池功能)
*/
public class DBManager {
// 配置信息
private static Configuration conf;
// 连接池对象
private static DBConnPool pool;
static {
// 静态代码块
Properties pros = new Properties();
try {
pros.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/sxt/SORM/db.properties"));
} catch (IOException e) {
e.printStackTrace();
}
conf = new Configuration();
conf.setDriver(pros.getProperty("driver"));
conf.setPoPackage(pros.getProperty("poPackage"));
conf.setPwd(pros.getProperty("pwd"));
conf.setSrcPath(pros.getProperty("srcPath"));
conf.setURL(pros.getProperty("URL"));
conf.setUser(pros.getProperty("user"));
conf.setUsingDb(pros.getProperty("usingDB"));
conf.setQueryClass(pros.getProperty("queryClass"));
conf.setPoolMaxSize(Integer.parseInt(pros.getProperty("poolMaxSize")));
conf.setPoolMinSize(Integer.parseInt(pros.getProperty("poolMinSize")));
// 加载TableContext
System.out.println(TableContext.class);
}
public static Connection getConn() {
/*获取数据库(mysql)驱动连接*/
// 加载驱动类
/* 第一种方法:直接取
try {
Class.forName(conf.getDriver());
// 直接建立连接,后期增加连接池处理,提高效率!
return DriverManager.getConnection(conf.getURL(), conf.getUser(), conf.getPwd());
} catch (Exception e) {
e.printStackTrace();
return null;
}
*/
// 第二种方法,通过连接池
if(pool == null){
pool = new DBConnPool();
}
return pool.getConnection();
}
/**
* 创建新的Connection连接
*
* @return
*/
public static Connection createConn() {
/*获取数据库(mysql)驱动连接*/
// 加载驱动类
try {
Class.forName(conf.getDriver());
// 直接建立连接,后期增加连接池处理,提高效率!
return DriverManager.getConnection(conf.getURL(), conf.getUser(), conf.getPwd());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void close(ResultSet rs, Statement ps, Connection conn) {
/**关闭接口方法*/
try {
if (rs != null) {
rs.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
if (ps != null) {
ps.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
// if (conn != null) {
// conn.close();
// }
pool.close(conn);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void close(Statement ps, Connection conn) {
/**关闭接口方法,重载*/
try {
if (ps != null) {
ps.close();
}
} catch (Exception e) {
e.printStackTrace();
}
try {
// if (conn != null) {
// conn.close();
// }
pool.close(conn);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void close(Connection conn) {
/*关闭接口方法,重载*/
try {
// if (conn != null) {
// conn.close();
// }
pool.close(conn);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Configuration getConf() {
return conf;
}
}
客户端测试
package com.sxt.SORM.test;
import com.sxt.SORM.core.Query;
import com.sxt.SORM.core.QueryFactory;
import com.sxt.SORM.vo.EmpVO;
import java.util.List;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: Test2.java
* @time: 2020/3/17 16:40
* @desc: |测试连接池的调用效率
*/
public class Test2 {
public static void test1(){
Query q = QueryFactory.createQuery();
String sql2 = "select e.id,e.empname,salary+bonus 'xinshui',age,d.dname 'deptName',d.address 'deptAddr' from emp e"
+ " " + "join dept d on e.deptId=d.id;";
List<EmpVO> list2 = q.queryRows(sql2, EmpVO.class, null);
for (EmpVO e : list2) {
System.out.println(e.getEmpname() + "-" + e.getDeptAddr() + "-" + e.getXinshui());
}
}
public static void main(String[] args){
long a = System.currentTimeMillis();
for (int i = 0; i < 3000; i++) {
test1();
}
long b = System.currentTimeMillis();
// 不加连接池的耗时:13077ms,增加连接池之后,耗时为2478
System.out.println(b-a);
}
}
idea导出参考连接:https://blog.csdn.net/rico_rico/article/details/84936785
javadoc导出参考连接:https://blog.csdn.net/qq_29347295/article/details/78635861
其中编码需要改为:-encoding utf-8 -charset utf-8
测试使用
配置配置文件
usingDB=mysql
URL=jdbc:mysql://localhost:3306/sorm?serverTimezone=UTC
driver=com.mysql.cj.jdbc.Driver
user=root
pwd=123456
srcPath=F:/BookStudy/else/SORMDemo/src
poPackage=po
queryClass=com.sxt.SORM.core.MysqlQuery
poolMinSize=10
poolMaxSize=100
测试po类的生成,增删改查
package test;
import com.sxt.SORM.core.Query;
import com.sxt.SORM.core.QueryFactory;
import po.Emp;
/**
* @author: Li Tian
* @contact: [email protected]
* @software: IntelliJ IDEA
* @file: Test.java
* @time: 2020/3/17 17:38
* @desc: |
*/
public class Test {
public static void main(String[] args) {
// 通过这个方法可以生成po类
// TableContext.updateJavaPOFile();
// add();
// select();
// delete();
update();
}
public static void add() {
// 测试插入对象
Emp e = new Emp();
e.setAge(18);
e.setEmpname("我");
e.setSalary(2000.0);
Query q = QueryFactory.createQuery();
q.insert(e);
}
public static void delete(){
// 测试删除对象
Emp e = new Emp();
e.setId(12);
Query q = QueryFactory.createQuery();
q.delete(e);
}
public static void update(){
// 测试删除对象
Emp e = new Emp();
e.setId(1);
e.setAge(1);
Query q = QueryFactory.createQuery();
q.update(e, new String[]{"age"});
}
public static void select(){
// 测试查询
Query q = QueryFactory.createQuery();
Number n = q.queryNumber("select count(*) from emp where salary>?", new Object[]{100});
System.out.println(n);
}
}
关于连表查询,我看操作实在是过于复杂,还要自己建立EmpVO,想想就算了。
我的CSDN:https://blog.csdn.net/qq_21579045
我的博客园:https://www.cnblogs.com/lyjun/
我的Github:https://github.com/TinyHandsome
纸上得来终觉浅,绝知此事要躬行~
欢迎大家过来OB~
by 李英俊小朋友
8 ↩︎
16 ↩︎
24 ↩︎
32 ↩︎