ORM 框架
除 Hibernate 和 MyBatis 外功能类似的 ORM 框架还有 Spring Data JPA、JPA、Apache Cayenne
Hibernate
组成
Hibernate 核心(Hibernate Core):
Hibernate 核心是整个框架的核心部分,提供了实现 ORM 功能的基本功能和核心类。它包括 SessionFactory、Session、Transaction 等核心接口和类,用于管理数据库连接、执行数据库操作、处理事务等。
Hibernate 配置(Hibernate Configuration):
Hibernate 配置模块负责加载和解析 Hibernate 的配置信息。它使用 hibernate.cfg.xml 配置文件或编程方式来配置 Hibernate,包括数据库连接信息、实体类映射配置、缓存策略、事务管理等。
ORM 映射(ORM Mapping):
ORM 映射模块负责将 Java 对象模型映射到关系数据库中。它提供了多种映射方式,如注解、XML 映射文件、hbm.xml 文件等,用于定义实体类与数据库表之间的映射关系、属性映射、关联关系等。
查询语言(Query Language):
Hibernate 提供了自己的查询语言,称为 Hibernate Query Language(HQL)。HQL 类似于 SQL,但使用实体类和属性名来操作数据库,具有面向对象的特性。HQL 可以执行各种查询操作,包括选择、过滤、排序和聚合等。
缓存(Cache):
缓存模块用于提高性能,减少数据库访问。Hibernate 提供了一级缓存和二级缓存。一级缓存是 Session 级别的缓存,存储被读取或写入的对象。二级缓存是 SessionFactory 级别的缓存,可跨 Session 共享数据。
事务管理(Transaction Management):
事务管理模块用于管理数据库事务。Hibernate 提供了Transaction
接口和相关方法,用于控制事务的开始、提交、回滚和隔离级别等。
工具类(Utility Classes):
Hibernate 还提供了一些实用工具类,用于处理日期、类型转换、异常处理等。它们包括SessionFactoryBuilder
、HibernateUtil
、HibernateException
等类。
Hibernate API 的组成
Configuration(配置)
Configuration 类用于配置和初始化 Hibernate。它允许开发人员指定数据库连接信息、实体类映射配置、缓存策略、事务管理等。
SessionFactory(会话工厂)
SessionFactory 是 Hibernate 的核心接口,用于创建和管理 Session 对象。SessionFactory 负责加载映射配置、建立数据库连接池、缓存数据和执行数据库操作。
Session(会话)
Session 是与数据库交互的主要接口。它代表了一次数据库会话,允许开发人员执行 CRUD 操作、查询、事务管理等。Session 是线程不安全的,通常使用单个 Session 与特定线程关联。
Transaction(事务)
Transaction 接口用于管理数据库事务。它提供了开始、提交、回滚和设置事务隔离级别等方法,用于保证数据的一致性和事务的原子性。
Query(查询)
Query 接口用于执行 HQL(Hibernate Query Language)查询。它允许开发人员执行各种查询操作,包括选择、过滤、排序和聚合等。Query 接口还提供了参数绑定、分页和缓存控制等功能。
Criteria(标准查询)
Criteria API 提供了一种面向对象的查询方式。它允许开发人员以类似于 SQL 的方式构建查询条件,通过 Criteria API 可以更直观地定义查询,避免了手写 HQL 或 SQL 语句。
Entity Manager(实体管理器)
Entity Manager 是 JPA(Java Persistence API)的一部分,它提供了对持久化对象的管理。在 Hibernate 中,Entity Manager 是由 Hibernate 实现的,用于管理实体的生命周期、执行持久化操作等
Hibernate 的关联映射相关的注解类
@OneToOne
: 建立一对一的关联映射。指定一个实体类属性与另一个实体类的主键之间的关联关系。
@OneToMany
: 建立一对多的关联映射。指定一个实体类拥有多个另一个实体类的实例。
@ManyToOne
: 建立多对一的关联映射。指定一个实体类拥有多个另一个实体类的实例。
@ManyToMany
: 建立多对多的关联映射。指定一个实体类与另一个实体类之间多对多的关系。
- 用于配置关联映射的注解,例如:
@JoinColumn
: 用于指定关联表中的外键列名。
@JoinTable
: 用于指定多对多关联映射中关联表的信息。
@Cascade
: 用于指定级联操作的行为,例如级联保存、更新、删除等。
Hibernate 的查询机制
- 导航对象图检索
- OID 检索
- HQL 检索
- QBC 检索
- 本地 SQL 检索
Hibernate Session API 的持久化方法和其功能
openSession()
打开一个新的连接,取得 Session 对象getCurrentSession()
使用一个事务管理器管理现有的 session 对象createQuery()
根据给定的 HQL 查询条件创建一个新的 Query 实例createSQLQuery()
根据给定的 SQL 查询条件创建一个新的 SQLQuery 实例persist()
将一个自由窗台的实例持久化,并引用配置的实体名参数
Hibernate 的主键生成器
@GeneratedValue(strategy = GenerationType.IDENTITY)
: 使用数据库的自增长主键来生成主键。@GeneratedValue(strategy = GenerationType.SEQUENCE)
: 使用数据库的序列来生成主键。@GeneratedValue(strategy = GenerationType.TABLE)
: 使用数据库的表来生成主键。@GeneratedValue(strategy = GenerationType.AUTO)
: 让 Hibernate 自动选择合适的主键生成策略。
- 此外,Hibernate 还提供了一些其他的主键生成策略:
@GeneratedValue(generator = "uuid")
: 使用 UUID(通用唯一标识符)来生成主键。@GenericGenerator(name = "custom_generator", strategy = "com.example.CustomIdGenerator")
: 使用自定义的主键生成器。
Hibernate 中进行对象持久化操作的方法
persist()
: 将一个新的对象保存到数据库中。与 save()方法类似,但是不返回生成的主键。
merge(
): 将一个游离状态(detached)的对象合并到持久化状态(persistent)。
remove()
: 从数据库中删除对象。
get()
: 根据主键立即获取一个对象。
flush()
: 将当前 Session 中的操作立即同步到数据库。
saveOrUpdate()
: 根据对象的状态,决定是保存新对象还是更新已存在的对象。
load()
: 根据主键延迟加载一个对象。
Hibernate 持久对象的状态和转换方法
持久对象有三种状态:瞬时状态、持久状态和游离状态
瞬时状态(Transient):
对象在内存中创建但未与数据库相关联。
没有关联的数据库标识符。
不受 Hibernate 会话的管理。
转换方法:
通过调用 save(), persist()或 saveOrUpdate()方法将瞬时对象转换为持久对象。
持久状态(Persistent):
对象已与数据库相关联。
有关联的数据库标识符。
受 Hibernate 会话的管理。
转换方法:
- 对象自动处于持久状态,当调用 save(), persist()或 saveOrUpdate()方法时。
- 通过调用 update()方法将游离对象转换为持久对象。
- 通过调用 merge()方法将游离对象合并为持久对象。
游离状态(Detached):
对象已与数据库断开关联。
有关联的数据库标识符。
不受 Hibernate 会话的管理。
转换方法:
- 通过调用 evict()方法将持久对象转换为游离对象。
- 通过调用 clear()方法将所有持久对象转换为游离对象。
- 通过调用 close()方法关闭会话,将所有持久对象转换为游离对象。
Hibernate 配置文件
默认 XML 配置文件名hibernate.cfg.xml
xml 文件读取方法
Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
默认属性配置文件名hibernate.properties
属性文件读取方法
Properties properties = new Properties();
try {
properties.load(getClass().getClassLoader().getResourceAsStream("hibernate.properties"));
} catch (IOException e) {
e.printStackTrace();
}
StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder();
registryBuilder.applySettings(properties);
SessionFactory sessionFactory = configuration.buildSessionFactory(registryBuilder.build());
HQL 中设置参数的方法
- 位置参数
使用?
来表示位置参数,然后通过setParameter(int position, Object value)
方法按照位置顺序设置参数的值。 - 命名参数
使用:parameterName
来表示命名参数,然后通过setParameter(String name, Object value)
方法按照参数名设置参数的值。
Hibernate 编程题(35 分)
- 以产品类 ProductModel、CategoryModel、AreaModel、PackagingModel 为例
ProductModel 对 CategoryModel 为多对一
ProductModel 对 AreaModel 为多对多
ProductModel 对 PackagingModel 为一对一
@Data //lombok生成getter、setter
@Entity //表名此类是实体类
@Table(name = "oa_product") //实体类在数据库中对应的表名
public class ProductModel {
@Id //表示此字段为主键
@GeneratedValue(strategy = GenerationType.IDENTITY) //表示增加新记录时的主键字段生成策略
@Column(name = "PNO") //此字段在数据库中的名称,若字段名于数据库中相同,可以不写此注解
private int id = 0;
@Column(name = "PNAME")
private String name = null;
@Column(name = "PRICE")
private double price = 0;
@Column(name = "QTY")
private int qty = 0;
@ManyToOne(fetch = FetchType.EAGER) //fetch,是否级联获取关联的对象,默认为FetchType.LAZY
@JoinColumn(name = "CNO")//此表中的外键
private CategoryModel category = null; //多对一中,对方“一方”的对象名
@ManyToMany //产品products对地区areas是多对多
//主动方在哪里,关联表的注解@JoinTable就在哪里,操作此实体类对象,关联的对方实体类对象会级联操作
@JoinTable( name = "erp_productarea", //多对多关联表的表名
joinColumns = @JoinColumn(name = "PNO"), //关联表中代表己方实体类的外键字段名
inverseJoinColumns = @JoinColumn(name = "ANo")) //关联表中代表对方实体类的外键字段名
private List<AreaModel> areas = null; //多对多对中,被动方的对象名
@OneToOne(mappedBy = "product", cascade = CascadeType.ALL, fetch = FetchType.EAGER)//产品对包装是一对一
private PackagingModel pack = null; //一对一中,被动方的对象名
}
- 品种实体类 CategoryModel
@Data
@Entity
@Table(name = "erp_category")
public class CategoryModel {
@Id
@GeneratedValue( strategy = GenerationType.IDENTITY )
private int CNo = 0;
private String CName = null;
@OneToMany(mappedBy = "category", fetch = FetchType.EAGER)//mappedBy为多对一关系中,多方实体类里定义的“一方”的对象名
private List<ProductModel> products = null;
}
- 地区实体类 AreaModel
@Data
@Entity(name = "product") //此实体类在hibernate中的别名,可有可无,便于编程
@Table(name = "erp_areas")
public class AreaModel {
@Id //主键字段对应的属性
@GeneratedValue(strategy = GenerationType.IDENTITY) //主键生成策略:由数据库生成
private int ANo = 0;
private String AName = null;
//多对多映射的被动方
@ManyToMany(mappedBy = "areas", fetch = FetchType.EAGER) //mappedBy为多对多关系中,多方实体类里定义的被动方的对象名
private List<ProductsModel> product = null;
}
- 包装实体类 PackagingModel
其中字段名 PNO 既为本实体类的主键,又作为外键指向 ProductModel 的主键
@Data
@Entity //告诉HIbernate此类是持久类
@Table(name = "erp_productpackaging")
public class PackagingModel {
@Id //主键字段对应的属性
//自定主键生成器策略
@GenericGenerator( name = "packKeyGenerator", //名称任意
strategy = "foreign", //策略为外键
parameters = @Parameter(name = "property", value = "product") //值为外键指向表的对象名
)
//使用自定主键生成器
@GeneratedValue(generator = "packKeyGenerator") //值为上面定义的自定义主键策略名
private int PNO = 0;//己方主键为对方外键
private String PTYPE = null;
private int PSIZE = 0;
@OneToOne
@PrimaryKeyJoinColumn //本实体类中的主键当外键时使用
private ProductsModel product = null; //一对一的主动方实体类对象
}
public class HibernateFactory {
private static final SessionFactory sf;
private static final Configuration cfg;
static{
//读取配置文件(如果有)hibernate.cfg.xml
cfg = new Configuration().configure();
//编程方式配置Hibernate
cfg.setProperty("hibernate.connection.driver_class", "com.mysql.cj.jdbc.Driver");
cfg.setProperty("hibernate.connection.url", "jdbc:mysql://localhost:端口号/数据库名?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC");
cfg.setProperty("hibernate.connection.username", "用户名");
cfg.setProperty("hibernate.connection.password", "密码");
//连接池可能为C3P0、HikariCP、Druid
cfg.setProperty("hibernate.连接池名.min_size", "1"); //最小连接数
cfg.setProperty("hibernate.连接池名.max_size", "5"); //最大连接数
cfg.setProperty("hibernate.连接池名.timeout", "500"); //超时时间
cfg.setProperty("show_sql", "true");
cfg.setProperty("format_sql", "true");
//MySQL方言配置
cfg.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
//model类映射
cfg.addAnnotatedClass(需要的Model类.class);
cfg.addAnnotatedClass(需要的Model类.class);
...
sf=cfg.buildSessionFactory();
}
public static SessionFactory createSessionFactory() throws Exception {
return sf;
}
}
public class ProductServiceImpl implements IProductService {
//无返回值的,如增删改
@Override
public void 方法名(对象类型 对象名) throws Exception {
Session session=HibernateFactory.openSession();
Transaction tx=session.beginTransaction();
session.persist(对象名); //增加
session.merge(对象名); //修改
session.remove(对象名); //删除
//细粒度的实体类拼装
ProductModel pm = new ProductModel();
pm.setId(19);
pm.setName("xxx");
session.persist(pm); //增加
tx.commit();
session.close();
}
//有返回值的,如各种查询
@Override
public 返回类型 方法名(对象类型 对象名) throws Exception {
Session session=HibernateFactory.openSession();
Transaction tx=session.beginTransaction();
// 1.构造HQL语句
String hql = "from XXXXModel where xx=:aaa and xx=:bbb ...";
// 2.建立请求,设置返回类型
Query<实体类型> query = session.createQuery(hql,实体类型.class);
// 3.设置请求参数
query.setParameter("aaa", xx);
query.setParameter("bbb", xx);
...
//4.
//取单个
对象类型 对象名 = query.getSingleResultOrNull();
//取多个
List<对象类型> resultList = query.getResultList();
tx.commit();
session.close();
return 返回对象;
}
}
特殊的,如分页,hibernate 自带分页查询
public List<对象类型> getListWithPage(int rows, int page) throws Exception {
Session session=HibernateFactory.openSession();
Transaction tx=session.beginTransaction();
String hql = "from 对象类型"; //hql中的对象类型,可以为XXXModel,也可以为别名(@Entity(name = "xxx"))
Query<对象类型> query = session.getCurrentSession().createQuery(hql,对象类型.class);
query.setFirstResult(rows*(page-1));
query.setMaxResults(rows);
List<对象类型> resultList = query.getResultList();
tx.commit();
session.close();
return resultList;
}
带事务控制的(失败即 Rollback)
@Transactional
public class ProductServiceImpl implements IProductService {
@Transactional(rollbackFor = Exception.class)
public 返回类型 方法名(对象类型 对象名) throws Exception {
...
}
}
带 spring 自动控制 session 注入的(不用写开启会话、关闭会话、提交事务)
@Service
@Transactional
public class ProductServiceImpl implements IProductService {
@Autowired
private SessionFactory sf = null;
public 返回类型 方法名(对象类型 对象名) throws Exception {
sf.persist(对象名);
sf.merge(对象名);
sf.remove(对象名);
Query<对象类型> query = sf.getCurrentSession().createQuery(hql,对象类型.class);
...
}
}
MyBatis
API 组成及其功能
SqlSessionFactoryBuilder:用于创建 SqlSessionFactory 实例的构建器。通过加载配置文件或者配置对象创建 SqlSessionFactory 实例。
SqlSessionFactory:用于创建 SqlSession 实例的工厂类。SqlSession 是 MyBatis 中最核心的接口之一。
SqlSession:用于执行 SQL 语句和管理事务的接口。它提供了许多方法来执行查询、插入、更新和删除操作,还包括提交事务、回滚事务等方法。
Executor:执行 SQL 语句的接口。SqlSession 中的方法最终都会委托给 Executor 来执行实际的 SQL 语句操作。
MappedStatement:映射 SQL 语句和方法的接口。它描述了 SQL 语句的信息,包括 SQL 语句的类型(增、删、改、查)、参数映射、结果映射等。
ParameterHandler:处理 SQL 语句中的参数的接口。它将 Java 对象映射为 SQL 语句中的占位符,并将参数传递给 JDBC 的 PreparedStatement 对象。
ResultSetHandler:处理 SQL 语句的结果集的接口。它将 JDBC 返回的 ResultSet 对象转换为 Java 对象或集合。
StatementHandler:处理 SQL 语句的接口。它负责创建 Statement 对象,并执行 SQL 语句。
MyBatis 的配置内容
数据源(dataSource)
数据源是用于与数据库建立连接的配置。MyBatis 支持三种类型的数据源:UNPOOLED(非池化)、POOLED(池化)和 JNDI(Java 命名和目录接口)。数据源配置中包含了数据库的驱动、连接 URL、用户名和密码等信息。
环境(environment)
MyBatis 允许配置多个环境,例如开发环境、测试环境和生产环境等。每个环境包含一个数据源和一个事务管理器。在配置时,需要指定一个默认环境。
事务管理器(transactionManager)
事务管理器负责管理数据库事务。MyBatis 提供了两种事务管理器类型:JDBC(直接使用 JDBC 的事务管理)和 MANAGED(由容器管理事务)。
类型别名(typeAliases)
类型别名是为 Java 类型设置一个短的别名。可以为频繁使用的 Java 类设置别名,以简化映射文件中的类型引用。别名可以通过配置文件进行设置,也可以通过注解的方式进行设置。
映射器(mappers)
映射器是 MyBatis 中用于关联实体类和数据库表的关键部分。映射器可以是 XML 文件,也可以是基于注解的 Java 接口。在配置文件中,需要将映射器文件或接口注册到 MyBatis 中,以便 MyBatis 能够找到并加载它们。
插件(plugins)
MyBatis 允许使用插件来扩展框架的功能。插件可以在配置文件中进行注册,并通过实现 Interceptor 接口来进行功能扩展。常见的插件有分页插件、性能监控插件等。
全局配置(settings)
全局配置包含了一些影响 MyBatis 框架运行行为的设置。例如,配置懒加载、缓存策略、映射结果集的策略等。
类型处理器(typeHandlers)
类型处理器负责在 Java 对象和数据库字段之间进行类型转换。MyBatis 内置了许多类型处理器,如日期类型、枚举类型等。如果需要自定义类型处理器,可以在配置文件中进行注册。
MyBatis 映射 XML 方式的主要标记
mapper
:是映射文件的根元素,用于指定映射文件与对应的接口类。通常通过namespace
属性来指定对应的接口全限定名。
select
:用于编写查询语句。它包含一个唯一的 id 属性,用于在接口中引用。resultType
属性用于指定返回的结果类型,可以是实体类的全限定名或者类型别名。resultMap
属性用于指定返回结果的映射关系。
insert
:用于编写插入语句。它同样包含一个唯一的 id 属性,用于在接口中引用。可选属性keyProperty
和useGeneratedKeys
用于配置自动生成的主键。
update
:用于编写更新语句。它包含一个唯一的 id 属性,用于在接口中引用。
delete
:用于编写删除语句。它包含一个唯一的 id 属性,用于在接口中引用。
resultMap
:用于描述查询结果与实体类之间的映射关系。它包含一个唯一的 id 属性,用于在select
标签中引用。type
属性用于指定实体类的全限定名或类型别名。result
子标签用于描述数据库列与实体类属性之间的映射关系。
sql
:用于定义可重用的 SQL 片段。它包含一个唯一的 id 属性,用于在其他 SQL 语句中引用。可以通过include
标签来引用定义好的 SQL 片段。
association
:用于描述一对一关联关系。通常用于嵌套查询或嵌套结果映射。
collection
:用于描述一对多关联关系。通常用于嵌套查询或嵌套结果映射。
mybatis 支持的 dao 接口的 sql 语句映射方式
XML 映射文件
在映射文件中,可以通过<insert>
,<update>
,<delete>
等元素定义 SQL 语句,并使用<resultMap>
,<parameterMap>
等元素进行结果映射和参数映射。
注解方式
使用注解来直接在 DAO 接口的方法上定义 SQL 语句。可以使用@Select
、@Insert
、@Update``、@Delete
等注解来标注方法,并在注解中指定 SQL 语句。
动态 SQL
在 XML 映射文件中使用<if>
,<choose>
,<foreach>
等元素来构建动态 SQL 语句。
注解与 XML 混合方式
可以在 DAO 接口的方法上使用注解定义部分 SQL 片段,而将复杂的 SQL 语句放在 XML 映射文件中进行配置。通过在 XML 映射文件中使用<include>
元素来引用注解定义的 SQL 片段。
MyBatis 的数据库连接池配置 mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:端口号/数据库名?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai"/>
<property name="username" value="用户名"/>
<property name="password" value="密码"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- Dao接口的映射 -->
<mapper resource="mapper/XXXMapper.xml"/>
</mappers>
</configuration>
MyBatis 编程题(30 分)
- mybatis 只有“对多”和“对一”关系(从实体类的第一人称出发)
以 StudentModel、ClassModel、CourseModel 为例
@Data
@Alias("student")
public class StudentModel {
private int no = 0;
private String name = null;
private int cno = 0;
private int age = 0;
private String sex = null;
//一个学生属于一个班级,对一
private ClassModel cls = null;
//一个学生可选多个课程,对多
private List<CourseModel> courses = null;
}
@Data
@Alias("class")
public class ClassModel {
private int no = 0;
private String name = null;
//一个班级包含多个学生,对多
private List<StudentModel> students = null;
}
@Data
@Alias("course")
public class CourseModel {
private int no = 0;
private String name = null;
//一个课程包含多个学生,对多
private List<StudentModel> students = null;
}
IStudentMapper 接口
@Mapper
public interface IStudentMapper {
// 增加学生
public void insert(StudentModel sm) throws Exception;
// 为学生增加选课。
public void insertCourse(@Param("studentNo") int studentNo,
@Param("courseNos") int[] courseNos
) throws Exception;
// 取得指定学生的选课列表
public List<CourseModel> selectCoursesByStudent(@Param("studentNo") int studentNo) throws Exception;
// 取得没有被选的选课列表
public List<CourseModel> selectCoursesWithoutSelected() throws Exception;
// 取得性别和课程的学生人数
public int selectStudentCountBySexAndCourse(@Param("sex") String sex,
@Param("courseNo") int courseNo
)throws Exception;
// 按检索条件取得学生列表,并取得关联的班级对象。
public List<StudentModel> selectListBySearchCondition(@Param("sex") String sex,
@Param("nameKeyword") String nameKeyword,
@Param("lowAge") int lowAge,
@Param("highAge") int highAge
)throws Exception;
// 按检索条件取得学生列表,并取得关联的班级对象和关联的课程集合。
public List<StudentModel> selectListBySearchCondition01(@Param("sex") String sex,
@Param("nameKeyword") String nameKeyword,
@Param("lowAge") int lowAge,
@Param("highAge") int highAge
) throws Exception
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.city.info.mapper.IStudentMapper">
<resultMap type="student" id="studentMap"> type对应model类名,或别名(若定义),id为此映射的名字
colum对应数据库中字段名,property对应model类中对象名
<id column="SSNO" property="no"/> id为主键标签。若有联合主键,可以有多个id标签
<result column="SNAME" property="name"/> result为普通字段标签,包括外键。
<result column="CNO" property="cno"/>
<result column="AGE" property="age"/>
<result column="SEX" property="sex"/>
</resultMap>
<!-- 此映射应该写在com.city.info.mapper.IClassMapper中,为了方便写在这里 -->
<resultMap type="class" id="classMap">
<id column="CNO" property="no"/>
<result column="CNAME" property="name"/>
</resultMap>
<!-- 此映射应该写在com.city.info.mapper.ICourseMapper中,为了方便写在这里 -->
<resultMap type="course" id="courseMap">
<id column="CCNO" property="no"/>
<result column="CCNAME" property="name"/>
</resultMap>
<insert id="insert">
insert into oa_student values(null,#{name},#{cno},#{age},#{sex})
</insert>
</mapper>
<resultMap type="student" id="studentWithClass" extends="studentMap"> extends为被扩展的映射名
<association property="cls" resultMap="classMap" /> 学生对班级,对一,resultMap对应对方映射名
</resultMap>
<resultMap type="student" id="studentWithCourses" extends="studentMap">
<collection property="courses" resultMap="courseMap" /> 学生对课程,对多,resultMap对应对方映射名
</resultMap>
<select id="selectCoursesByStudent" resultMap="studentWithCourses"> 注意映射id
select co.CCNAME
from oa_course co, oa_student st, oa_studentcourse sc
where co.CCNO=sc.CCNO and st.SSNO=sc.SSNO and sc.SSNO=#{studentNo}
</select>
<select id="selectCoursesWithoutSelected" resultMap="courseMap">
SELECT * FROM oa_course WHERE CCNO not in (SELECT DISTINCT CCNO FROM oa_studentcourse)
</select>
<select id="selectStudentCountBySexAndCourse" resultType="int"> 注意是resultType还是resultMap
SELECT count(*)
from oa_course co, oa_student st, oa_studentcourse sc
WHERE co.CCNO=sc.CCNO and st.SSNO=sc.SSNO and st.SEX=#{sex} and co.CCNO=#{courseNo}
</select>
动态插入
<insert id="insertCourse">
insert into oa_studentcourse values
<foreach collection="courseNos" item="no" separator=","> collection是传入的数组名,item是数组中的元素定义在model类中的字段名
(#{no},#{studentNo})
</foreach>
</insert>
动态查询
<select id="selectListBySearchCondition" resultMap="studentWithClass">
SELECT st.SSNO, st.SNAME, st.CNO, st.AGE, st.SEX, cl.CNAME
from oa_student st, oa_class cl
<where> 条件查询
st.CNO=cl.CNO
<if test="sex!='' or sex!=null">
and st.sex=#{sex}
</if>
<if test="nameKeyword!='' and nameKeyword!=null">
and st.SNAME like concat('%',#{nameKeyword},'%')
</if>
<if test="lowAge!=0">
and st.AGE >= #{lowAge} >为<
</if>
<if test="highAge!=0">
and st.AGE <= #{highAge} <为>
</if>
</where>
group st.SSNO
<if test="upDown!=null and upDown!=''"> 升降序
order by st.AGE
<if test="upDown=='asc'">asc</if>
<if test="upDown=='desc'">desc</if>
</if>
limit #{start},#{rows} 分页
</select>