JDBC之DAO层封装思想超详解
Posted siaok
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDBC之DAO层封装思想超详解相关的知识,希望对你有一定的参考价值。
mysql版本:8.0.26
可视化客户端:sql yog
编译软件:IntelliJ IDEA 2019.2.4 x64
运行环境:win10 家庭中文版
jdk版本:1.8.0_361
目录
一、DAO是什么?
众所周知,Java是面向对象语言,数据在Java中通常是以对象的形式存在。而在数据库中,它的每张数据表我们可以视作是Java中的一个个类,而表的每一行记录和相应的字段,便可视作是一个个的对象和对象的属性。相应地字段值,便是对象的属性值。反之,Java的对象也可看作是数据库中数据表的每一行记录,而Java对象的属性名和属性值,就是记录的字段名和相应的字段值。
当Java程序通过JDBC与数据库交互时,我们把涉及
访问数据库的代码封装起来,这些类称为DAO
,英文全称为Data Access Object。它相当于是一个数据访问接口,夹在业务逻辑与数据库资源中间,这样使得代码结构更加清晰、易于维护和扩展。
如下图所示:
二、案例演示
案例:尝试连接数据库0225db,以DAO层的封装思想的形式进行编码,访问部门表与员工表,实现增删改查相应的功能
2.1 准备数据
①建立员工表t_employee
Create Table
CREATE TABLE `t_employee` (
`eid` int NOT NULL AUTO_INCREMENT COMMENT '员工编号',
`ename` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '员工姓名',
`salary` double NOT NULL COMMENT '薪资',
`commission_pct` decimal(3,2) DEFAULT NULL COMMENT '奖金比例',
`birthday` date NOT NULL COMMENT '出生日期',
`gender` enum('男','女') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '男' COMMENT '性别',
`tel` char(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '手机号码',
`email` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '邮箱',
`address` varchar(150) DEFAULT NULL COMMENT '地址',
`work_place` set('北京','深圳','上海','武汉') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '北京' COMMENT '工作地点',
`hiredate` date NOT NULL COMMENT '入职日期',
`job_id` int DEFAULT NULL COMMENT '职位编号',
`mid` int DEFAULT NULL COMMENT '领导编号',
`did` int DEFAULT NULL COMMENT '部门编号',
PRIMARY KEY (`eid`),
KEY `job_id` (`job_id`),
KEY `did` (`did`),
KEY `mid` (`mid`),
CONSTRAINT `t_employee_ibfk_1` FOREIGN KEY (`job_id`) REFERENCES `t_job` (`jid`) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT `t_employee_ibfk_2` FOREIGN KEY (`did`) REFERENCES `t_department` (`did`) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT `t_employee_ibfk_3` FOREIGN KEY (`mid`) REFERENCES `t_employee` (`eid`) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT `t_employee_chk_1` CHECK ((`salary` > 0)),
CONSTRAINT `t_employee_chk_2` CHECK ((`hiredate` > `birthday`))
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
②建立部门表t_department
CREATE TABLE `t_department` (
`did` int NOT NULL AUTO_INCREMENT COMMENT '部门编号',
`dname` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '员工名称',
`description` varchar(200) DEFAULT NULL COMMENT '员工简介',
PRIMARY KEY (`did`),
UNIQUE KEY `dname` (`dname`)
) ENGINE=InnoDB AUTO_INCREMENT=1012 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
2.2 创建bean包
目的:
创建两个类用于封装从t_employee和t_department拿过来的每一行记录
代码演示如下:
①创建department类,用于封装从t_department拿过来的每一行记录,同时一个department类的对象可以写入t_department表里
public class department
private Integer did;
private String dname;
private String description;
public department()
public department(int did)
this.did = did;
public department(int did, String dname, String description)
this.did = did;
this.dname = dname;
this.description = description;
public department(String dname, String description)
this.dname = dname;
this.description = description;
public int getDid()
return did;
public void setDid(int did)
this.did = did;
public String getDname()
return dname;
public void setDname(String dname)
this.dname = dname;
public String getDescription()
return description;
public void setDescription(String description)
this.description = description;
@Override
public String toString()
return "department" +
"did=" + did +
", dname='" + dname + '\\'' +
", description='" + description + '\\'' +
'';
②创建employee类,用于封装从t_employee拿过来的每一行记录,同时一个employee类的对象可以写入t_employee表里
import java.math.BigDecimal;
import java.util.Date;
public class employee
private Integer eid;
private String ename;
private Double salary;
private BigDecimal commissioPct;
private Date birthday;
private String gender;
private String tel;
private String email;
private String address;
private String workPlace="北京";
private Date hiredate;
private Integer jobId;
private Integer mid;
private Integer did;
public employee()
public employee(Integer eid)
this.eid = eid;
public employee(String ename, Double salary, BigDecimal commissioPct, Date birthday, String gender, String tel, String email, String address, String workPlace, Date hiredate, Integer jobId, Integer mid, Integer did)
this.ename = ename;
this.salary = salary;
this.commissioPct = commissioPct;
this.birthday = birthday;
this.gender = gender;
this.tel = tel;
this.email = email;
this.address = address;
this.workPlace = workPlace;
this.hiredate = hiredate;
this.jobId = jobId;
this.mid = mid;
this.did = did;
public employee(Integer eid, String ename, Double salary, BigDecimal commissioPct, Date birthday, String gender, String tel, String email, String address, String workPlace, Date hiredate, Integer jobId, Integer mid, Integer did)
this.eid = eid;
this.ename = ename;
this.salary = salary;
this.commissioPct = commissioPct;
this.birthday = birthday;
this.gender = gender;
this.tel = tel;
this.email = email;
this.address = address;
this.workPlace = workPlace;
this.hiredate = hiredate;
this.jobId = jobId;
this.mid = mid;
this.did = did;
public Integer getEid()
return eid;
public void setEid(Integer eid)
this.eid = eid;
public String getEname()
return ename;
public void setEname(String ename)
this.ename = ename;
public Double getSalary()
return salary;
public void setSalary(Double salary)
this.salary = salary;
public BigDecimal getCommissioPct()
return commissioPct;
public void setCommissioPct(BigDecimal commissioPct)
this.commissioPct = commissioPct;
public Date getBirthday()
return birthday;
public void setBirthday(Date birthday)
this.birthday = birthday;
public String getGender()
return gender;
public void setGender(String gender)
this.gender = gender;
public String getTel()
return tel;
public void setTel(String tel)
this.tel = tel;
public String getEmail()
return email;
public void setEmail(String email)
this.email = email;
public String getAddress()
return address;
public void setAddress(String address)
this.address = address;
public String getWorkPlace()
return workPlace;
public void setWorkPlace(String workPlace)
this.workPlace = workPlace;
public Date getHiredate()
return hiredate;
public void setHiredate(Date hiredate)
this.hiredate = hiredate;
public Integer getJobId()
return jobId;
public void setJobId(Integer jobId)
this.jobId = jobId;
public Integer getMid()
return mid;
public void setMid(Integer mid)
this.mid = mid;
public Integer getDid()
return did;
public void setDid(Integer did)
this.did = did;
@Override
public String toString()
return "employee" +
"eid=" + eid +
", ename='" + ename + '\\'' +
", salary=" + salary +
", commission_pct=" + commissioPct +
", birthday=" + birthday +
", gender='" + gender + '\\'' +
", tel='" + tel + '\\'' +
", email='" + email + '\\'' +
", address='" + address + '\\'' +
", work_place='" + workPlace + '\\'' +
", hiredate=" + hiredate +
", job_id=" + jobId +
", mid=" + mid +
", did=" + did +
'';
2.3 建立DAO包
2.2.1 建立BaseDAOImpl类
目的:
基本上每一个数据表都应该有一个对应的DAO接口及其实现类,发现对所有表的操作(增、删、改、查)代码重复度很高,所以可以抽取公共代码,给这些DAO的实现类可以抽取一个公共的父类,称为BaseDAOImpl。该类包含通用的增删改查的方法。
代码演示如下:
import util.JDBCTools;
import util.JDBCTools2;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public abstract class BaseDAOImpl
//通用的曾,删,改操作
//sql中包含? 参数,需要设置?的值
//如果sgL中没有?,那么调用这个方法时,不用传args对应的实参
// 如果sgL中有5个?,那么调用这个方法时,要给args传入对应的5个实参
//实现流程 : java程序 ----> mysql数据库中的数据表
public int update(String sql,Object ...args) throws SQLException
//连接数据库,类似于网络编程中的socket
//使用JDBCTools(1.0)获取的连接对象
Connection conn = JDBCTools.getConnection();
//使用JDBCTools2(2.0)获取的连接对象
// Connection conn = JDBCTools2.getConnection();
//将sql语句预编译
PreparedStatement pst = conn.prepareStatement(sql);
if (args!=null && args.length>0)
for (int i = 0; i < args.length; i++)
pst.setObject(i+1,args[i]);//设置?的值
int len= pst.executeUpdate();//执行sql语句并返回同步更新的记录条数
//释放连接
// JDBCTools.freeConnection(conn);
return len;
//查询多个对象的信息的通用方法
//实现流程: Mysql数据库中的数据表的记录 ---> java程序中
protected <T> List<THibernate 学习笔记
Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,它将 POJO 与数据库表建立映射关系,是一个全自动的 orm 框架
hibernate(持久化)
一. Hibernate 的理解
Hibernate 是数据访问层(Dao层),就是把数据存入到数据库中,称为持久化。
Hibernate 对 JDBC 进行了封装,针对数据访问层提出面向对象的思想,操作对象间接的操作数据库中的表,自动生成 SQL 语句,可以简化数据访问层的代码,提高开发效率。
二. hibernate的优缺点
-
优点:
-
使用 JDBC 遇到的问题:
-
代码中存在大量的 SQL 语句
-
查询结果需要手动封装到 Model
-
SQL 语句中存储大量的?
,需要手动赋值
-
SQL 语句根据不同的数据库,有不同的函数,如果更换数据库,需要大量更改 SQL 语句,针对数据库移植性差
-
使用 Hibernate 可以解决以上问题:
-
Hibernate 操作对象自动生成 SQL 语句
-
查询结果自动赋值给 Model 类
-
自动赋值
-
Hibernate 使用的是 HQL 语句,根据不同的方言,生成不同数据库的 SQL 语句,到达跨数据库平台
注:数据迁移是软性项目中的大事情,特别难做,做项目时对数据库的选型尤为重要
-
缺点:
- SQL语句自动生成,人工无法控制,使得 SQL 语句执行效率慢
- Hibernate 执行效率低
- Hibernate 特别耗内存,有一系列缓存机制
三. 什么是ORM(☆)
ORM 是一种编程思想(开发模式),全称:Object Relation Mapper(对象关系映射)
是为了解决面向对象与面向关系型数据库不匹配现象,通过一个配置文件把面向对象与面向关系型数据库关联起来
- 类 --- 表
- 属性 --- 字段
- 对象 --- 记录
优点:使得数据访问层更面向对象,不用考虑关系型数据库,只要会面向对象即可,开发程序变简单了(Hibernate、MyBatis、Spring、JDBC)
四、搭建 Hibernate 环境
- 导包:在
hibernate-release-4.3.11.Final/lib/required
下所有 Jar 文件与数据库驱动包复制到项目中
- 引入 Hibernate 主配置文件,
Hibernate.cfg.xml
文件复制到项目中的 src 目录
- 创建一个 Model 类实现 Serializable 接口,对应一个表,并且在映射文件
xxx.hbm.xml
中配置
- 编写 Hibernate API 进行测试
五. Hibernate 的体系结构(Hibernate由哪几部分组成)(☆)
结构
描述
hibernate.cfg.xml
是 Hibernate 的主配置文件,用来配置数据库连接信息与 Hibernate 相关参数
XXX.hbm.xml
类与表之间的映射文件,一个类对应一个表,一个属性对应一个字段
实体类
用于封装数据库的表,一定要实现 Serializable 接口
Hibernate API
用于读取并解析配置文件,然后根据映射关系生成 SQL 语句,间接操作数据库
六. Hibernate 工作原理(☆)
- Configuration 类读取并解析配置文件(主配置文件、映射文件)
- 创建 SessionFactory(一个 SessionFactory 对应一个数据库)
- 打开 Session(Session 代表程序与数据库之间的一次会话,用来做表的增删改查)
- 创建事务(Transaction 代表数据库事务)
- 持久化操作(增删改查)
- 提交事务
- 关闭 Session
- 当应用程序停止,关闭 SessionFactory
注:SessionFactory 是一个重量级对象,创建销毁特别耗资源,应用程序创建一次,关闭一次
七. Hibernate 代码工作原理
-
获取 Configuration 类的对象(cfg)
-
调用 cfg.configure()
方法默认读取 src 根目录下的 hibernate.cfg.xml
-
调用cfg.buildSessionFactory()
方法创建 Session 工厂(sf)
-
调用sf.openSession()
方法获取 Session
-
若为添加、删除、修改操作,则开启事务
Transaction ts = session.getTransaction()
ts.begin();
-
进行持久化操作
添加:e(obj);
删除:delete(obj);
修改:update(obj);
查询:get(类名.class, 1);
查询:load(类名.class, 1);
添加和修改都支持:saveOrUpdate(obj);
-
提交事务,关闭 Session,关闭 SessionFactory
ts.commit();
session.close();
sf.close();
八. Hibernate 的映射类型
Hibernate 映射文件xxx.hbm.xml
中,Java 数据类型与数据库数据类型相互转换,通过 type 属性进行指定,类型如下:
-
整数
Java 类型
type 取值
byte
byte
short
short
int
Integer
long
long
-
小数
Java 类型
type 取值
float
float
double
double
-
字符串
Java 类型
type 取值
String
String
-
日期
Java 类型
type 取值
java.util.Date
date
java.sql.Date
date
-
时间
Java 类型
type 取值
java.sql.Timestamp
timestamp
注:建议手动创建表,不要让 Hibernate 生成表格,hibernate.hbm2ddl.auto
配置成为 update
九. Hibernate 核心开发接口(常用的类与接口)(☆)
接口
描述
Configuration
读取并解析配置文件,然后创建 SessionFactory(是一个类)
SessionFactory
代表一个数据库,一个 SessionFactory 对应一个数据库,用于创建 Session(是一个接口)
Session
程序与数据库的一次会话,用于数据库表的增删改查(是一个接口)
Transaction
事务管理(是一个接口)
Query 与 Criteria
用于数据查询(是一个接口)
十. Hibernate 有多少种查询
- 主键查询:
get()
与load()
- HQL 查询
- SQL 查询
- QBC 查询
查询效率由高到低排列:主键查询 > SQL 查询 > HQL 查询 > QBC 查询
十一. 查询方法延迟加载(☆)
-
什么是延迟加载?
Hibernate 中存在一些方法,在查询的时候并没有马上发送 SQL 语句去数据库查询,而是返回一个空值对象,空值对象不是 null,而是新 new 出来的对象,除主键以外其他属性值为空,当程序真正使用到此对象时,才会发送 SQL 语句去数据库中查询,并且将结果赋值给此对象,这种查询称为延迟加载
-
为什么要用延迟加载?
- 在延迟的这段时间内,并没有访问数据库,可以节约内存开销,减少数据库的访问,提高使用效率
- 可以防止查询对象,但并没有真正的使用,这种情况下可以节约内存,减少数据库访问
-
如何使用延迟加载?
-
Hibernate 中一些方法自带延迟加载机制,只要调用这些方法,就可以使用延迟加载
-
具有延迟加载机制的访问如下
延迟加载
立即加载
session.load()
session.get()
query.iterate()
query.list()
-
get() 与 load() 区别(☆)
get():立即加载,立即发送 SQL 语句查询,如果没有查询到结果返回 null
load():延迟加载,不会立即发送 SQL 语句查询,返回一个空值对象,真正使用到此对象,才会发送 SQL 语句。如果没有查询到结果,会抛出ObjectNotFoundException
异常
-
list() 与 iterate() 区别
list():立即加载,立即发送 SQL 语句查询,返回一个对象集合,如果没有查询到数据,返回空集合
iterate():延迟加载,首先会发送一条 SQL 语句把表中所有主键查询出来,在遍历的时候,根据主键发送 SQL 语句单个查询,有多少条记录就会发送多少条 SQL 语句查询
注:建议使用立即加载
十二. 一级缓存(☆)
-
什么是一级缓存?
Hibernate 在创建 Session 的时候,会给每个 Session 另外分配一片内存空间,用于缓存 Session 操作过的对象,这块儿内存称为一级缓存,一级缓存是
Session 管理并且使用的,所以也称为:Session 缓存。
一级缓存的生命周期与 Session 一致,Session 被创建时,一级缓存空间被分配,session.close()
时,一级缓存被回收
-
一级缓存的作用?
一级缓存用来缓存 Session 操作过的对象,相同数据不用每次都去查询数据库,直接从一级缓存中获取,提高查询效率
-
一级缓存的步骤?
Session 优先查询一级缓存,首先去一级缓存中查询,查询不到才会发送 SQL 语句查询数据库,查询到结果之后会在一级缓存中存放一份,再次查询时,无需发送 SQL 语句查询数据库,直接从一级缓存中获取(在 Hibernate 中一级缓存优先于数据库,一级缓存与数据库数据不同步时,以一级缓存为主)
-
如何使用一级缓存?
-
一级缓存是默认开启,自动使用
-
一级缓存的特征:
- 一级缓存是 Session 独享的,Session 与 Session 之间不能共享数据
- Session 查询一组数据时,会将一组数据拆开存入一级缓存中,一级缓存中存储的是单个对象
- 执行增删改时会同步一级缓存,当
delete(Object obj)
时,在一级缓存中会标记此对象可能被删除,再次查询时不会发送 SQL 语句查询,返回一个null 对象
-
管理缓存
- clear():清空一级缓存
- evict():清空一级缓存单个对象
- flush():手动同步一级缓存与数据库,数据不一样,以一级缓存为主
十三. 二级缓存
SessionFactory 级别的缓存(需要配置)
Hibernate 二级缓存需要配置,在同一 SessionFactory 范围内,查询同一个对象多次,只会发送一条 SQL 语句(只会去数据库中查询一次),后面每次查询都是从二级缓存中去取数据
配置信息:
- 导入二级缓存的 Jar 包 ehcache oscache
- 把对应配置文件放入到 src 根目录下
- 在
Hibernate.cfg.xml
中开启二级缓存
- 声明哪些对象需要放入到二级缓存中
十四. 对象的持久性
什么是对象的持久性?
Hibernate 操作对象时,可以把对象看成三种状态::瞬时态,持久台,游离态/托管态
三种状态的规则?
-
瞬时态(transient)
- 定义:对象刚刚被 new 出来,称为瞬时态
- 规则:瞬时态可以被垃圾回收机制回收,一级缓存中没有,数据库中没有
-
持久态(persistent)
- 定义:一级缓存中有,数据库中有,称为持久态
- 规则:通过 save(),update(),saveOrUpdate(),get(),load(),HQL,SQL,QBC 方式操作过的对象,称为持久态对象
-
游离态(detached)
- 定义:一级缓存中没有,数据库中有,称为游离态
- 规则:通过 clear,evict,close 方式操作过的对象, 称为游离态对象
十五. 主键的生成策略
在映射文件(xxx.hbm.xml)中需要配置主键字段,并且需要配置主键的生成策略,通过 generator 标签指定
主键生成策略:
-
sequence
代码如下:
<generator class="sequence">
<!-- 指定序列的名称 -->
<param name="sequence">t_person_seq</param>
</generator>
对应数据库:Oracle,DB2
-
identity
代码如下:
<generator class="identity"></generator>
对应数据库:MySQL,SQL Server
-
native
代码如下:
<generator class="native"></generator>
含义:native 是让 hibernate 自动选择一种主键方式,根据配置文件中的方言,从 sequence 与 identity 中选一个,方言配置的是 Oracle ,自动选择sequence,方言配置的是MySQL,自动选择 identity
-
assigned
代码如下:
<generator class="assigned"></generator>
含义:程序员手动分配主键,Hibernate 不会自动生成
-
uuid
代码如下:
<generator class="uuid"></generator>
含义:采用 UUID 算法生成一个32位十六进制的字符串作为主键
-
increment
代码如下:
<generator class="increment"></generator>
含义:查询表中最大 id 值, 把最大 id + 1 生成主键
优点:适用与任何数据库
缺点:并发量大时,会产生相同的 id,线程不安全,不推荐使用
十六. 关系映射
什么是关系映射?
如果两张表之间有关系,Hibernate 允许我们将关系提取出来,并且映射的配置文件中,在对一个表的增删改查操作,Hibernate 通过这个映射关系,间接的操作另一张表的增删改查,这两个表的关系配置称为关系映射
关系映射类型?
- 多对一
- 一对多
- 一对一
- 多对多
十七. 多对一(重点)与一对多
-
设计
两张表之间的关系为多对一或一对多,会在多的一端增加一个字段指向一的那端的主键
-
案例
学生与班级:多个学生属于一个班级,一个班级有多个学生
一对多添加会产生 N+1 条,尽量少用一对多,用多对一代替
一对多配置:
<!--
set标签:指定是 set 集合
name属性:类中的属性名
-->
<set name="students">
<!--
key标签:指定两个表之间关联字段(外键字段名称)
column属性:外键字段的字段名
-->
<key column="f_classId"></key>
<!--
one-to-many标签:表示与哪个类发生了一对多的关系
class属性:多对那端类的名称
-->
<one-to-many class="Student" />
</set>
多对一配置:
<!--
many-to-one 标签:多对一关系
name 属性:类中属性的名称
column 属性:表中的字段名(外键字段名)
class 属性:外键属性的类(完整路径)
-->
<many-to-one name="cla" column="f_classId" class="com.zt.model.Classes"></many-to-one>
十八. 关联操作
关联查询(查询)
-
延迟加载(对于关联属性,Hibernate 默认采用的是延迟加载,查询一端数据,不会关联出另一端数据)
lazy="proxy/true" 默认方式,采用懒加载
lazy="false" 立即加载,关联的表数据会同时查询出来
-
连接查询
fetch="select" 默认方式,使用多条 SQL 语句查询
fetch="join" 使用连接查询,一条 SQL 语句完成查询,使用此属性懒加载失效
-
join 查询
String hql = "from Student s left join fetch s.cla"; 左外连接
String hql = "from Student s inner join fetch s.cla"; 内连接
级联操作(增删改)
-
什么是级联操作?
在对一张表做增删改操作时,关联的另一张表也做增删改操作,称为级联操作
-
如何设置级联操作?
在映射文件中,关联映射配置 cascade 属性,用这个属性定义级联操作
-
cascade取值如下:
取值方式
描述
none
默认方式,不支持级联
all
支持增删改
save-update
支持增,改
delete
支持删
十九. 一对一(了解)
一对一的类型:
主键一对一:包装两个标段主键相同
外键一对一:在任意一端增加一个字段(外键字段)指向另一端的主键,并且这个字段有唯一约束(不能重复)
案例:
学生与档案一对一,在学生表中增加一个字段指向档案表的主键,外键保证唯一性
二十. 多对多
什么是多对多?
如果两张表的关系是多对多,必然产生一张中间表,中间表只有两个字段,分别为两张表的外键字段,这两个外键字段组合成复合主键
案例:
一个班级由多个老师教学,一个老师可以教多个班级
多对多配置:
<!--
set标签:指定是 set 集合
name属性:类中的属性名
table:中间表的表名
-->
<set name="teachers" table="t_cla_tea">
<!--
column属性:当前表的主键的对应中间表的外键字段
-->
<key column="f_classId"></key>
<!--
many-to-many标签:多对多的关系映射
class属性:当前 set 集合元素对应的类名
column属性:对方表的主键对应中间表的外键字段名
-->
<many-to-many class="Teacher" column="f_teacherId"/>
</set>
二十一. Hibernate 查询语句
- 主键查询:load,get
- HQL 查询(Hibernate Query Language) :标准的SQL + 面向对象语言
- QBC 查询(Query By Criteria):完全的面向对象
- SQL 查询(Structured Query Language):结构化查询语言
本文来自博客园,作者:Schieber,转载请注明原文链接:https://www.cnblogs.com/xiqingbo/p/java-24.html
以上是关于JDBC之DAO层封装思想超详解的主要内容,如果未能解决你的问题,请参考以下文章