1.ORM简介
- ORM是对象关系映射(Object-Relational Mapping)的缩写
- 就是把数据库中的表映射为java的类,表中的记录映射为java的对象,列映射为java类中的属性
- 该框架可以在不写SQL语句的前提下完成对单表的CRUD操作
需要使用的技术:泛型 注解 反射 JDBC技能
2.创建maven工程并引入相关依赖
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
</dependencies>
3.创建一个连接数据库的工具类
db.properties
jdbc.driverName=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=root
DbUtil.javapackage com.wqg.util; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Properties; public class DbUtil { //硬编码放入属性文件db.properties private static String driverName = ""; private static String url = ""; private static String username = ""; private static String password = ""; static { //静态代码块。----随着类的加载而被加载,而且只会被加载一次。 try { InputStream inputStream = ClassLoader.getSystemResourceAsStream("db.properties"); Properties properties = new Properties(); properties.load(inputStream); driverName = properties.getProperty("jdbc.driverName"); url = properties.getProperty("jdbc.url"); username = properties.getProperty("jdbc.username"); password = properties.getProperty("jdbc.password"); } catch (Exception e) { System.out.println("名字为db.properties的属性文件不存在"); e.printStackTrace(); } } //获取连接对象 public static Connection getConnection() throws Exception { Class.forName(driverName); Connection connection = DriverManager.getConnection(url, username, password); return connection; } //关闭资源 public static void closeAll(Connection connection, PreparedStatement ps, ResultSet rs) { try { if (rs != null) { rs.close(); } } catch (Exception e) { e.printStackTrace(); } try { if (ps != null) { ps.close(); } } catch (Exception e) { e.printStackTrace(); } try { if (connection != null) { connection.close(); } } catch (Exception e) { e.printStackTrace(); } } }
4.自定义注解
(1).自定义主键注解
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface TableId { String value() default "id"; //主键名默认id //使用此注解标记主键,默认自增,数据库不是自增不能使用 }
(2).自定义属性注解
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface TableField { String value(); } //解决列名与属性名不一致的注解 (实体类与数据库名不一致)
(3).自定义表名注解
@Target(ElementType.TYPE) //只能用在类上 @Retention(RetentionPolicy.RUNTIME) //运行时有效 public @interface TableName { String value(); }
5.创建一个父类BaseDao
一个子类DAO继承BaseDao类后,无需再写任何sql语句,而就可以完成单表的crud操作。
比如: StudentDao extends BaseDao
BaseDao对所有单表的CRUD操作都能通用。
5.1.编写通用的添加操作
package com.wqg.dao; import com.wqg.annotation.TableField; import com.wqg.annotation.TableId; import com.wqg.annotation.TableName; import com.wqg.util.DbUtil; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.PreparedStatement; import java.util.ArrayList; import java.util.List; public class BaseDao<T> { //添加操作 //insert into 表名(列名,列名....) values(值,值....) public int insert(T t) throws Exception { StringBuffer sql = new StringBuffer("insert into "); //根据对象获取Class反射类 Class<?> aClass = t.getClass(); //获取反射类上的注解对象 TableName annotation = aClass.getAnnotation(TableName.class); //获取表名 String tableName = aClass.getSimpleName(); if (annotation != null) { tableName = annotation.value(); } sql.append(tableName); //表名拼接SQL上 //获取列名 Field[] declaredFields = aClass.getDeclaredFields(); //存放列名 List<String> columns = new ArrayList<>(); //列值 List<String> values = new ArrayList<>(); for (Field field : declaredFields) { String name = field.getName();//属性名(列名) TableId annotation2 = field.getAnnotation(TableId.class); if (annotation2 != null || name.equals("id")) { continue; } TableField annotation1 = field.getAnnotation(TableField.class); if (annotation1 != null) { name = annotation1.value(); //注解的value值 } field.setAccessible(true); Object o = field.get(t);//获取属性对应的值 columns.add(name); values.add("'" + o + "'"); } //列名 String columnNames = columns.toString().replace("[", "(").replace("]", ")"); //列值 String columnValues = values.toString().replace("[", "(").replace("]", ")"); //列名和列值拼接SQL上 sql.append(columnNames); sql.append(" values "); sql.append(columnValues); System.out.println(sql); //执行SQL Connection connection = DbUtil.getConnection(); PreparedStatement ps = connection.prepareStatement(sql.toString()); int i = ps.executeUpdate(); return i; } }
5.2.编写通用的修改操作
//修改操作 //update 表名 set 列名 = 值 , 列名 = 值..... where 主键列 = 主键值 public int update(T t) throws Exception { StringBuffer sql = new StringBuffer("update "); //获取实体类的反射类 Class<?> aClass = t.getClass(); //获取反射类上指定的注解对象 TableName annotation = aClass.getAnnotation(TableName.class); String tableName = aClass.getSimpleName(); if (annotation != null) { tableName = annotation.value(); } sql.append(tableName + " set "); //获取所有的Field Field[] declaredFields = aClass.getDeclaredFields(); String where = " where "; for (Field field : declaredFields) { field.setAccessible(true); String name = field.getName(); TableId tableId = field.getAnnotation(TableId.class); if (name.equals("id")) { where = where + " id='" + field.get(t) + "'"; continue; } if (tableId != null) { where = where + tableId.value() + " = '" + field.get(t) + "'"; continue; } TableField tableField = field.getAnnotation(TableField.class); if (tableField != null) { name = tableField.value(); } String value = "'" + field.get(t) + "'"; sql.append(name + "=" + value + ","); } String sql2 = sql.toString().substring(0, sql.length() - 1) + where; System.out.println(sql2); //执行SQL Connection connection = DbUtil.getConnection(); PreparedStatement ps = connection.prepareStatement(sql2); int i = ps.executeUpdate(); return i; }
5.3.编写通用的删除操作
//为获取表名 // 总体而言,这段代码的目的是通过反射获取当前类继承的父类的泛型参数类型,并将其存储在aCl变量中。 // 具体来说,这段代码适用于继承了BaseDao的子类,通过子类的构造函数获取泛型参数的类型。 Class<T> clazz; //泛型的反射类 public BaseDao(){ //获取BaseDao子类的反射类对象 Class<? extends BaseDao> aClass = this.getClass(); //获取当前Dao子类的父类的反射类 ParameterizedType parameterizedType = (ParameterizedType) aClass.getGenericSuperclass(); //得到父类反射类上的泛型的反射类 Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); clazz = (Class<T>) actualTypeArguments[0]; }
//删除操作 //delete from 表名 where 主键列=主键值 public int delete(Object id) throws Exception{ StringBuffer sql = new StringBuffer("delete from "); TableName annotation = clazz.getAnnotation(TableName.class); String tableName = clazz.getSimpleName(); if (annotation != null){ tableName=annotation.value(); } sql.append(tableName+" where "); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields){ TableId tableId = field.getAnnotation(TableId.class); if (tableId != null){ sql.append(tableId.value()+"='"+id+"'"); continue; } } //执行SQL Connection connection = DbUtil.getConnection(); PreparedStatement ps = connection.prepareStatement(sql.toString()); int i = ps.executeUpdate(); return i; }
5.4.编写通用的单个查询操作
//查询(根据主键单个查询) //select * from 表名 where 主键列=值 //定义一个泛型方法,根据传入的id查询数据并返回 public T selectById(Object id) throws Exception { //创建一个StringBuffer对象,并添加查询语句 StringBuffer sql = new StringBuffer("select * from "); //获取当前类中的@TableName注解 TableName annotation = clazz.getAnnotation(TableName.class); //获取当前类的简单名称 String tableName = clazz.getSimpleName(); //如果@TableName注解不为空,则将其值赋给tableName if (annotation != null) { tableName = annotation.value(); } //将tableName添加到sql语句中,并添加查询条件 sql.append(tableName + " where "); //获取当前类中的所有属性 Field[] fields = clazz.getDeclaredFields(); //遍历属性数组 for (Field field : fields) { //获取当前属性的@TableId注解 TableId tableId = field.getAnnotation(TableId.class); //如果@TableId注解不为空,则将其值添加到sql语句中 if (tableId != null) { sql.append(tableId.value() + "='" + id + "'"); continue; } } //执行SQL语句 Connection connection = DbUtil.getConnection(); PreparedStatement ps = connection.prepareStatement(sql.toString()); ResultSet resultSet = ps.executeQuery(); //定义一个泛型变量t,并初始化为null T t = null; //遍历结果集 while (resultSet.next()) { //创建一个新的实例 t = clazz.newInstance(); //获取当前实例中的所有属性 Field[] declaredFields = clazz.getDeclaredFields(); //遍历属性数组 for (Field field : declaredFields) { //设置属性可访问 field.setAccessible(true); //获取属性名称 String name = field.getName(); //获取当前属性的@TableField注解 TableField tableField = field.getAnnotation(TableField.class); //如果@TableField注解不为空,则将其值赋给name if (tableField != null) { name = tableField.value(); } //获取当前属性的@TableId注解 TableId tableId = field.getAnnotation(TableId.class); //如果@TableId注解不为空,则将其值赋给name if (tableId != null) { name = tableId.value(); } //获取结果集中指定列的值 Object v = resultSet.getObject(name); //将属性值赋给实例 field.set(t, v); } } //返回实例 return t; }
5.5.编写通用的查询所有操作
//查询所有 //select * from 表名 public List<T> findAll() throws Exception { StringBuffer stringBuffer = new StringBuffer("select * from "); List<T> list = new ArrayList<>(); //定义一个泛型集合,用来存储查找到的结果 Connection connection = null; //定义连接对象,初始值为null PreparedStatement ps = null; //定义预编译对象,初始值为null ResultSet rs = null; //定义结果集对象,初始值为null //获取表名 TableName tableName = (TableName) clazz.getAnnotation(TableName.class); if (tableName == null) { throw new RuntimeException("未使用TableName注解标记表名"); } String name = tableName.value(); //拿到表名 stringBuffer.append(name); try { connection = DbUtil.getConnection(); //通过工具类获取数据库连接 ps = connection.prepareStatement(stringBuffer.toString()); //预编译SQL语句 rs = ps.executeQuery(); //执行查询语句,获取结果集 while (rs.next()) { //遍历结果集 T t = (T) clazz.newInstance(); //使用反射机制创建一个对象实例 Field[] declaredFields = clazz.getDeclaredFields(); //获取反射类中所有的属性对象 for (Field field : declaredFields) { //遍历所有属性对象 field.setAccessible(true); //设置属性可以访问 TableId tableId = field.getAnnotation(TableId.class); //获取属性上的主键注解 if (tableId != null) { //如果主键注解不为空 field.set(t, rs.getObject(tableId.value())); //设置属性值为结果集中对应主键列的值 } else { //如果该属性不是主键字段 TableField tableField = field.getAnnotation(TableField.class); //获取除主键外的属性上的TableField注解 if (tableField != null) { //如果TableField注解不为空 field.set(t, rs.getObject(tableField.value())); //设置属性值为结果集中对应列的值 } else { //如果该属性既不是主键字段,也没有TableField注解 field.set(t, rs.getObject(field.getName())); //设置属性值为结果集中对应列的值,列名和属性名一致 } } } list.add(t); //将对象添加到泛型集合中 } } catch (Exception e) { //捕获异常 e.printStackTrace(); //打印异常栈信息 } finally { //无论是否发生异常,都执行finally中的代码 // DbUtil.closeAll(rs,ps,connection); //关闭结果集、预编译对象、连接 DbUtil.closeAll(connection, ps, rs); } return list; //返回泛型集合 }
6.导出jar包(存储ORM框架)
7.导入ORM框架的jar包
在其他工程中使用ORM框架,只需要导入jar包,配置为库
配置相关依赖和db.properties文件(上面第3条有)
8.测试ORM的CRUD
导入ORM的jar包,配置pom依赖,配置为库,配置db.properties文件...
数据库表:
创建Student.java
@TableName("tbl_student") public class Student { @TableId(value = "sid") private Integer sid; @TableField(value = "sname") private String sName; @TableField(value = "class_id") private Integer classId; public Student() { } public Student(Integer sid, String sName, Integer classId) { this.sid = sid; this.sName = sName; this.classId = classId; } /** * 获取 * @return sid */ public Integer getSid() { return sid; } /** * 设置 * @param sid */ public void setSid(Integer sid) { this.sid = sid; } /** * 获取 * @return sName */ public String getSName() { return sName; } /** * 设置 * @param sName */ public void setSName(String sName) { this.sName = sName; } /** * 获取 * @return classId */ public Integer getClassId() { return classId; } /** * 设置 * @param classId */ public void setClassId(Integer classId) { this.classId = classId; } public String toString() { return "Student{sid = " + sid + ", sName = " + sName + ", classId = " + classId + "}"; } }
创建StudentDao.java
public class StudentDao extends BaseDao<Student> { }
创建Test.java测试CRUD
public class Test { public static void main(String[] args) throws Exception { StudentDao studentDao = new StudentDao(); //查询所有 //System.out.println(studentDao.findAll()); //根据id查询单个 //System.out.println(studentDao.selectById(1)); //增加 //studentDao.insert(new Student(6,"马保",1)); //修改 //studentDao.update(new Student(1,"LDH66",2)); //删除 //studentDao.delete(5); } }