commit 3aeda46b0b780d1080fc1455ee1de19caeeaa26d Author: frank <3224536684@qq.com> Date: Wed Jun 28 00:54:18 2023 +0800 :sparkles: first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a879ee7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/ +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..d7a78a7 --- /dev/null +++ b/pom.xml @@ -0,0 +1,80 @@ + + + 4.0.0 + + net.javase.db + mysql-tool + 1.0.0 + + + 8 + 8 + UTF-8 + 8.0.30 + + + + + junit + junit + 4.13.2 + test + + + mysql + mysql-connector-java + ${mysql.version} + runtime + + + commons-beanutils + commons-beanutils + 1.9.4 + + + + + + mysql-tool + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${maven.compiler.source} + ${maven.compiler.target} + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.1 + + + package + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.1.0 + + + nexus-release + package + + jar-no-fork + + + + + + + + \ No newline at end of file diff --git a/src/main/java/net/javase/db/annotation/Column.java b/src/main/java/net/javase/db/annotation/Column.java new file mode 100644 index 0000000..73d8993 --- /dev/null +++ b/src/main/java/net/javase/db/annotation/Column.java @@ -0,0 +1,38 @@ +package net.javase.db.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Column + * + * @author frank + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Column { + + /** + * column name + * + * @return column name + */ + String name(); + + /** + * not null + * + * @return true | false + */ + boolean notNull() default false; + + /** + * default value + * + * @return default value + */ + String defaultValue(); + +} diff --git a/src/main/java/net/javase/db/annotation/Entity.java b/src/main/java/net/javase/db/annotation/Entity.java new file mode 100644 index 0000000..b3a0ac0 --- /dev/null +++ b/src/main/java/net/javase/db/annotation/Entity.java @@ -0,0 +1,24 @@ +package net.javase.db.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 实体类注解 + * + * @author frank + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Entity { + + /** + * 自定义表名 + * + * @return table name + */ + String table() default ""; + +} diff --git a/src/main/java/net/javase/db/annotation/Id.java b/src/main/java/net/javase/db/annotation/Id.java new file mode 100644 index 0000000..dbd1656 --- /dev/null +++ b/src/main/java/net/javase/db/annotation/Id.java @@ -0,0 +1,17 @@ +package net.javase.db.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 主键注解 + * + * @author frank + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Id { + +} diff --git a/src/main/java/net/javase/db/config/DbConfig.java b/src/main/java/net/javase/db/config/DbConfig.java new file mode 100644 index 0000000..d09b434 --- /dev/null +++ b/src/main/java/net/javase/db/config/DbConfig.java @@ -0,0 +1,102 @@ +package net.javase.db.config; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +/** + * DbConfig + * + * @author Frank + */ +public class DbConfig { + private static final String FILE_NAME = "mysql.properties"; + + private static Properties p = null; + + private static DbConfig dbConfig = null; + + /** + * driver + */ + private String driver; + + /** + * url + */ + private String url; + + /** + * username + */ + private String username; + + /** + * password + */ + private String password; + + public DbConfig(String driver, String url, String username, String password) { + this.driver = driver; + this.url = url; + this.username = username; + this.password = password; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getDriver() { + return driver; + } + + public void setDriver(String driver) { + this.driver = driver; + } + + static { + InputStream inputStream = ClassLoader.getSystemResourceAsStream(FILE_NAME); + p = new Properties(); + try { + p.load(inputStream); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 获取配置信息对象 + */ + public static DbConfig get() { + if (dbConfig == null) { + dbConfig = new DbConfig( + p.getProperty("driver"), + p.getProperty("url"), + p.getProperty("username"), + p.getProperty("password") + ); + } + return dbConfig; + } +} diff --git a/src/main/java/net/javase/db/utils/BeanUtil.java b/src/main/java/net/javase/db/utils/BeanUtil.java new file mode 100644 index 0000000..54732d8 --- /dev/null +++ b/src/main/java/net/javase/db/utils/BeanUtil.java @@ -0,0 +1,5 @@ +package net.javase.db.utils; + +public class BeanUtil { + +} diff --git a/src/main/java/net/javase/db/utils/DbUtil.java b/src/main/java/net/javase/db/utils/DbUtil.java new file mode 100644 index 0000000..596db0b --- /dev/null +++ b/src/main/java/net/javase/db/utils/DbUtil.java @@ -0,0 +1,297 @@ +package net.javase.db.utils; + +import net.javase.db.annotation.Column; +import net.javase.db.annotation.Entity; +import net.javase.db.annotation.Id; +import net.javase.db.config.DbConfig; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.sql.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.beanutils.PropertyUtils; + +/** + * DbUtil + * + * @author Frank + */ +public class DbUtil { + + private static Connection conn; + + static { + try { + Class.forName(DbConfig.get().getDriver()); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + + /** + * 获取数据库连接对象 + * + * @return {@link Connection} + */ + public static Connection getConnection() { + // 获取连接并捕获异常 + try { + if (conn == null || conn.isClosed()) + conn = DriverManager.getConnection( + DbConfig.get().getUrl(), + DbConfig.get().getUsername(), + DbConfig.get().getPassword()); + } catch (SQLException e) { + e.printStackTrace(); + } + return conn;// 返回连接对象 + } + + /** + * 关闭数据库连接 + * + * @param conn {@link Connection} 数据库连接 + */ + public static void closeConn(Connection conn) { + try { + if (conn != null && !conn.isClosed()) + conn.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 关闭数据库连接。 + * + * @param conn 数据库连接 + * @param stmt Statement对象 + * @param rs 结果集 + */ + public static void closeAll(Connection conn, Statement stmt, ResultSet rs) { + // 若结果集对象不为空,则关闭 + try { + if (rs != null && !rs.isClosed()) + rs.close(); + } catch (Exception e) { + e.printStackTrace(); + } + // 若Statement对象不为空,则关闭 + try { + if (stmt != null && !stmt.isClosed()) + stmt.close(); + } catch (Exception e) { + e.printStackTrace(); + } + // 若数据库连接对象不为空,则关闭 + try { + if (conn != null && !conn.isClosed()) + conn.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 增、删、改操作 + * + * @param sql sql语句 + * @param params 参数数组 + * @return 执行结果 + */ + public static int executeUpdate(String sql, Object... params) { + int result = 0; + conn = getConnection(); + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement(sql); + for (int i = 0; i < params.length; i++) { + pstmt.setObject(i + 1, params[i]); + } + result = pstmt.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } finally { + closeAll(null, pstmt, null); + } + return result; + } + + /** + * 查询操作 + * + * @param sql sql语句 + * @param params 参数数组 + * @return 查询结果集 + */ + public static ResultSet executeQuery(String sql, Object... params) { + conn = getConnection(); + PreparedStatement pstmt = null; + ResultSet rs = null; + try { + pstmt = conn.prepareStatement(sql); + for (int i = 0; i < params.length; i++) { + pstmt.setObject(i + 1, params[i]); + } + rs = pstmt.executeQuery(); + } catch (SQLException e) { + e.printStackTrace(); + } + return rs; + } + + /** + * 查询实体列表 + * + * @param T + * @param clazz entity class + * @param sql 查询sql + * @param params sql参数 + * @return entity list + */ + public static List selectList(Class clazz, String sql, Object... params) { + conn = getConnection(); + PreparedStatement pstmt = null; + ResultSet rs = null; + List list = new ArrayList(); + try { + pstmt = conn.prepareStatement(sql); + for (int i = 0; i < params.length; i++) { + pstmt.setObject(i + 1, params[i]); + } + rs = pstmt.executeQuery(); + Map entityMateData = getMetaData(clazz); + ResultSetMetaData rsmd = rs.getMetaData(); + int columnCount = rsmd.getColumnCount(); + while (rs.next()) { + T obj = clazz.newInstance(); + for (int i = 1; i <= columnCount; i++) { + String columnName = rsmd.getColumnName(i); + Object value = rs.getObject(i); + try { + if (entityMateData.get(columnName) != null) { + PropertyUtils.setProperty(obj, entityMateData.get(columnName), value); + } + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } + list.add(obj); + } + entityMateData = null; + } catch (SQLException | InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } finally { + closeAll(conn, pstmt, rs); + } + return list; + } + + /** + * 获取entity对应的column对应关系 + * + * @param T + * @param clazz entity class + * @return map + */ + private static Map getMetaData(Class clazz) { + Map map = new HashMap<>(); + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + Column c = field.getAnnotation(Column.class); + if (c != null) { + if (StrUtil.notBlank(c.name())) { + map.put(c.name(), field.getName()); + } + } + map.put(field.getName(), field.getName()); + } + return map; + } + + /** + * 生成建表语句 + * + * @param clazz 实体类 + * @param underline 是否下划线命名 + * @param 实体类 + * @return sql + */ + public static String createSchemaSql(Class clazz, boolean underline) { + String table = clazz.getSimpleName(); + Entity e = clazz.getAnnotation(Entity.class); + if (e != null && !StrUtil.isBlank(e.table())) { + table = e.table(); + } else { + table = StrUtil.camelToUnderscore(table); + } + StringBuilder sqlBuilder = new StringBuilder(); + sqlBuilder.append("CREATE TABLE IF NOT EXISTS `").append(table).append("` ("); + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + String columnName = field.getName(); + String jdbcType = ""; // 类型 + boolean notNull = false; // 是否为空 + String defaultValue = ""; // 默认值 + boolean isKey = false; + + Id id = field.getAnnotation(Id.class); + if (id != null) { + isKey = true; + notNull = true; + } + + Column c = field.getAnnotation(Column.class); + if (c != null) { + if (StrUtil.notBlank(c.name())) { + columnName = c.name(); + } + notNull = c.notNull(); + defaultValue = c.defaultValue(); + } + + Class fieldType = field.getType(); + if (String.class.equals(fieldType)) { + jdbcType = "VARCHAR(255)"; + } else if (Integer.class.equals(fieldType) || int.class.equals(fieldType)) { + jdbcType = "INT"; + } else if (Long.class.equals(fieldType) || long.class.equals(fieldType)) { + jdbcType = "BIGINT"; + } else if (Boolean.class.equals(fieldType) || boolean.class.equals(fieldType)) { + jdbcType = "BOOLEAN"; + } else if (Double.class.equals(fieldType) || double.class.equals(fieldType)) { + jdbcType = "DOUBLE"; + } else if (Date.class.equals(fieldType) || java.util.Date.class.equals(fieldType)) { + jdbcType = "datetime"; + } else if (Float.class.equals(fieldType) || float.class.equals(fieldType)) { + jdbcType = "float"; + } else { + jdbcType = "VARCHAR(255)"; + } + + if (underline) { + columnName = StrUtil.camelToUnderscore(columnName); + } + + sqlBuilder.append("`").append(columnName).append("` ") + .append(jdbcType).append(" ") + .append(notNull ? "NOT NULL" : "NULL").append(" ") + .append(StrUtil.notBlank(defaultValue) ? "DEFAULT '" + defaultValue + "'" : "").append(" ") + .append(isKey ? "PRIMARY KEY AUTO_INCREMENT" : "") + .append(","); + } + // 去掉最后一个逗号 + if (fields.length > 0) { + sqlBuilder.deleteCharAt(sqlBuilder.length() - 1); + } + + sqlBuilder.append(") ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + return sqlBuilder.toString(); + } +} diff --git a/src/main/java/net/javase/db/utils/StrUtil.java b/src/main/java/net/javase/db/utils/StrUtil.java new file mode 100644 index 0000000..8742cb4 --- /dev/null +++ b/src/main/java/net/javase/db/utils/StrUtil.java @@ -0,0 +1,86 @@ +package net.javase.db.utils; + +/** + * StrUtil + * + * @author Frank + */ +public class StrUtil { + + /** + * 判断字符串是否为空 + * + * @param str 字符串 + * @return true | false + */ + public static boolean isBlank(String str) { + return str == null || str.length() == 0 || str.trim().length() == 0; + } + + /** + * 字符串是否不为null或者'' + * + * @param str 字符串 + * @return true | false + */ + public static boolean notBlank(String str) { + return !isBlank(str); + } + + /** + * 驼峰转下划线 + * + * @param str 驼峰字符串 + * @return 下划线字符串 + */ + public static String camelToUnderscore(String str) { + if (str == null || str.isEmpty()) { + return str; + } + StringBuilder result = new StringBuilder(); + char[] chars = str.toCharArray(); + for (int i = 0; i < chars.length; i++) { + char ch = chars[i]; + if (i == 0) { + result.append(Character.toLowerCase(ch)); + continue; + } + if (Character.isUpperCase(ch)) { + result.append("_").append(Character.toLowerCase(ch)); + } else if (Character.isDigit(ch)) { + result.append("_").append(ch); + } else { + result.append(ch); + } + } + return result.toString(); + } + + /** + * 下划线转驼峰 + * + * @param underLineStr 下划线字符串 + * @return 驼峰字符串 + */ + public static String underlineToCamel(String underLineStr) { + StringBuilder result = new StringBuilder(); + // 标记当前字符是否为下划线后的第一个字符 + boolean flag = false; + // 遍历字符串,将下划线后的第一个字符转换成大写字母 + for (int i = 0; i < underLineStr.length(); i++) { + char currentChar = underLineStr.charAt(i); + if (currentChar == '_') { + flag = true; + } else { + if (flag) { + result.append(Character.toUpperCase(currentChar)); + flag = false; + } else { + result.append(Character.toLowerCase(currentChar)); + } + } + } + return result.toString(); + } + +} diff --git a/src/test/java/net/javase/db/test/BankAccount.java b/src/test/java/net/javase/db/test/BankAccount.java new file mode 100755 index 0000000..6775b57 --- /dev/null +++ b/src/test/java/net/javase/db/test/BankAccount.java @@ -0,0 +1,85 @@ +package net.javase.db.test; + +import net.javase.db.annotation.Entity; +import net.javase.db.annotation.Id; + +import java.util.Date; + +/** + * BankAccount + * @author frank + */ +@Entity +public class BankAccount { + + @Id + private int id; + + private String username; + + private String realName; + + private String password; + + private String optPassword; + + private String balance; + + private Date createTime; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getRealName() { + return realName; + } + + public void setRealName(String realName) { + this.realName = realName; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getOptPassword() { + return optPassword; + } + + public void setOptPassword(String optPassword) { + this.optPassword = optPassword; + } + + public String getBalance() { + return balance; + } + + public void setBalance(String balance) { + this.balance = balance; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } +} diff --git a/src/test/java/net/javase/db/test/BankAccountLog.java b/src/test/java/net/javase/db/test/BankAccountLog.java new file mode 100755 index 0000000..4f22faa --- /dev/null +++ b/src/test/java/net/javase/db/test/BankAccountLog.java @@ -0,0 +1,86 @@ +package net.javase.db.test; + +import net.javase.db.annotation.Entity; +import net.javase.db.annotation.Id; + +import java.util.Date; + +/** + * BankAccountLog + * + * @author frank + */ +@Entity +public class BankAccountLog { + + @Id + private int id; + + private int accountId; + + private String username; + + private String optType; + + private String optAmount; + + private String desc; + + private Date createTime; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getAccountId() { + return accountId; + } + + public void setAccountId(int accountId) { + this.accountId = accountId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getOptType() { + return optType; + } + + public void setOptType(String optType) { + this.optType = optType; + } + + public String getOptAmount() { + return optAmount; + } + + public void setOptAmount(String optAmount) { + this.optAmount = optAmount; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } +} diff --git a/src/test/java/net/javase/db/test/DbUtilTest.java b/src/test/java/net/javase/db/test/DbUtilTest.java new file mode 100644 index 0000000..0f2b1d5 --- /dev/null +++ b/src/test/java/net/javase/db/test/DbUtilTest.java @@ -0,0 +1,28 @@ +package net.javase.db.test; + +import net.javase.db.utils.DbUtil; + +import java.util.List; + +import org.junit.Test; + +/** + * MainTest + * + * @author Frank + */ +public class DbUtilTest { + + @Test + public void testCreateTableSql() { + System.out.println(DbUtil.createSchemaSql(BankAccount.class, true)); + System.out.println("\n"); + System.out.println(DbUtil.createSchemaSql(BankAccountLog.class, true)); + } + + @Test + public void testQueryList() { + List list = DbUtil.selectList(Product.class, "select * from sys_product"); + list.forEach(item -> System.out.println(item)); + } +} diff --git a/src/test/java/net/javase/db/test/Product.java b/src/test/java/net/javase/db/test/Product.java new file mode 100644 index 0000000..b50a4d7 --- /dev/null +++ b/src/test/java/net/javase/db/test/Product.java @@ -0,0 +1,52 @@ +package net.javase.db.test; + +public class Product { + + private Integer pid; + + private String pname; + + private Double pprice; + + private Integer stock; + + public String getPname() { + return pname; + } + + public void setPname(String pname) { + this.pname = pname; + } + + public Integer getPid() { + return pid; + } + + public void setPid(Integer pid) { + this.pid = pid; + } + + public Double getPprice() { + return pprice; + } + + public void setPprice(Double pprice) { + this.pprice = pprice; + } + + public Integer getStock() { + return stock; + } + + public void setStock(Integer stock) { + this.stock = stock; + } + + @Override + public String toString() { + return "Product [pid=" + pid + ", pname=" + pname + ", pprice=" + pprice + ", stock=" + stock + "]"; + } + + + +} diff --git a/src/test/resources/mysql.properties b/src/test/resources/mysql.properties new file mode 100644 index 0000000..1f34a23 --- /dev/null +++ b/src/test/resources/mysql.properties @@ -0,0 +1,4 @@ +driver=com.mysql.cj.jdbc.Driver +url=jdbc:mysql://localhost:3306/demo?characterEncoding=utf-8&serverTimezone=GMT%2b8 +username=root +password=root \ No newline at end of file