文章目录
1.文件目录标准结构
- 项目包结构
控制层 com.system.controiler
逻辑业务层 com.system.service(接口类所放的包名)com.system.service.impl(实现类所放的包名)
数据访问层 com.system.dao(接口类所放的包名)com.system.dao.impl(实现类所放的包名)
- Mapper命名要求
- 要求1:Mapper接口类中命名规则为:***Mapper.xml,接口中声明的是对数据库操作的方法
- 要求2: 实体映射文件的命名要与所定义的接口名字一致!!namespace属性值必须是对应Mapper接口的全路径!!SQL查询的id和参数也要与接口中的方法一致!!(使得该xml文件与对应的接口类建立联系)
2.优化配置文件格式
- 优化1:优化主配置文件SqlMapConfig.xml中的映射器结构
未优化前:
优化后:让系统自动扫描该包下的xml文件<mappers> <mapper resource="com/system/mapper/EmpMapper.xml" /> <mapper resource="com/system/mapper/DeptMapper.xml" /> </mappers>
<mappers> <package name="com.system.mapper"/> </mappers>
- 优化2:优化映射文件中接口类名字的书写
通过在主配置文件SqlMapConfig.xml中,定义实体类的别名,即不用在映射文件中写实体类的全路径名(让系统自动扫描并命别名,别名即为实体类命,首字符大小写均可)<typeAliases> <package name="com.system.entity"/> </typeAliases>
- 优化3:数据库配置的优化(让数据库配置信息与xml文件分离)
- 让数据库的配置信息配置在独立文件:db.properties (该后缀名配置文件以键值对的形式,建=值)例如:
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/empdb?characterEncoding=utf8 jdbc.username=root jdbc.password=设置的密码
- 然后在主配置文件SqlMapConfig.xml中导入db.properties
<!-- 导入db.properties --> <properties resource="db.properties"></properties>
- 数据源的配置,使用${键}的形式来引用properties文件中的值
<dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource>
3.创建Mapper代理
- Mapper代理通俗来说就是通过调用所定义好的接口中的方法来实现相关业务。而方法的实现则在XML文件中定义(或使用注解定义)
//创建SqlSessionFactory工厂类可封装成一个Util类
try {
InputStream input = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(input);
SqlSession sqlSession = sqlSessionFactory.openSession();//获得会话对象
//使用Mapper代理
DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);//获得了DeptMapper接口类的代理
} catch (IOException e) {
e.printStackTrace();
}
4.利用Mapper代理进行增、删、改、查
1)增
- 在对应接口的映射文件中
<insert id="insertDept" parameterType="com.system.entity.Dept">
insert into dept (dname,loc) values (#{dname},#{loc})
</insert>
- 也可以在接口类中用注解的方式(注解的方式适合简单的单表查询!!)
@Insert("insert into dept (dname,loc) values (#{dname},#{loc})")//只需将SQL语句写入,参数与返回值会自动与接口参数匹配!
@Options(useGeneratedKeys = true, keyProperty = "deptno")//其他参数
public void insertDept(Dept dept);
2)删
- 在对应接口的映射文件中
<delete id="deleteDept" parameterType="Integer">
delete from dept where deptno = #{deptno}
</delete>
- 也可以在接口类中用注解的方式(注解的方式适合简单的单表查询!!)
@Delete("delete from dept where deptno = #{deptno}")
public int deleteDept(Integer deptno);
3)改
- 在对应接口的映射文件中
<update id="updateDept" parameterType="com.system.entity.Dept">
update dept set dname=#{dname},loc=#{loc} where deptno = #{deptno}
</update>
- 也可以在接口类中用注解的方式(注解的方式适合简单的单表查询!!)
@Update("update dept set dname=#{dname},loc=#{loc} where deptno = #{deptno}")
public int updateDept(Dept dept);
4)查
a)单表查询
- 在对应接口的映射文件中
<select id="getDeptAll" resulType="com.system.entity.Dept">
select * from dept
</select>
- 也可以在接口类中用注解的方式(注解的方式适合简单的单表查询!!)
@Select("select * from dept")
public List<Dept> getDeptAll();
b)多表查询
通过修改实体类来表示数据库中表与表之间的关系(一对多、一对一、多对多,在实体类中定义另一个实体类的对象),以一对多(两个实体类之间的表达该关系有些许差异)举例
//实体类中表示一对多的关系,一个员工从属于一个部门,一个部门下有多个员工
//部门类
public class Dept {
private Integer deptno;//部门编号
private String dname;//部门名称
private String loc;//所在城市
private List<Emp> empList;//定义集合,一个部门下有多个员工,一对多
}
//员工类
public class Emp {
private Integer empno;//员工编号
private String ename;//员工姓名
private String job;//职位
private double sal;//工资
private double comm;//奖金
private Integer deptno;//部门编号
//属于某个部门
private Dept dept;
}
自定义返回类型
多表查询中,其返回结果“类中有类”,因此需要自定义返回类型,本质就是自定义属性匹配规则!!
- 一对多 :查询所有员工的员工信息以及对应的部门信息
在映射文件中自定义返回值类型
<!-- 自定义返回值类型 -->
<resultMap id="EmpDeptResultMap" type="Emp"> <!-- id为返回值的类型名,type为“类中有类”的“外类”名 -->
<id property="empno" column="empno"></id> <!-- property为类中的属性名,column为对应数据库中的列名 -->
<result property="ename" column="ename"></result>
<result property="job" column="job"></result>
<result property="sal" column="sal"></result>
<result property="comm" column="comm"></result>
<result property="deptno" column="deptno"></result>
<association property="dept" javaType="Dept">
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
</association>
</resultMap>
<!-- 一次性查询 -->
<select id="allEmpDept" resultMap="EmpDeptResultMap"> <!-- 返回值为自定义类型 -->
select * from emp left join dept on emp.deptno=dept.deptno
</select>
- 多对一:查询所有部门信息以及每个部门下对应的员工信息
<!-- 自定义返回值类型 -->
<resultMap id="DeptEmpResultMap" type="Dept">
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
<collection property="empList" ofType="Emp"> <!-- 这里对应的是一个list集合 -->
<id property="empno" column="empno"></id>
<result property="ename" column="ename"></result>
<result property="job" column="job"></result>
<result property="sal" column="sal"></result>
<result property="comm" column="comm"></result>
<result property="deptno" column="deptno"></result>
</collection>
</resultMap>
<select id="allDeptEmp" resultMap="DeptEmpResultMap">
select * from dept left join emp on emp.deptno=dept.deptno
</select>
分步查询
- 日志输出
可在主配置文件中添加以下配置,用于日志输出,观察SQL语句的执行情况
<!-- 日志输出 -->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
- 一对多:查询所有员工的员工信息以及对应的部门信息(step1-查询员工信息、step2-根据员工信息中的部门编号查询对应的部门信息)
step1: 修改association标签,增加select、colum属性,select的值为SQL语句所在的映射文件的方法名,colum为传递参数,可理解为“外键”值,这里为部门编号
<!-- 分步查询 -->
<resultMap id="EmpDeptResultMap" type="Emp">
<id property="empno" column="empno"></id>
<result property="ename" column="ename"></result>
<result property="job" column="job"></result>
<result property="sal" column="sal"></result>
<result property="comm" column="comm"></result>
<result property="deptno" column="deptno"></result>
<association property="dept" javaType="Dept" select="com.system.mapper.DeptMapper.getDeptByDeptno" column="deptno"></association>
</resultMap>
<select id="allEmpDept" resultMap="EmpDeptResultMap">
select * from emp
</select>
step2: 在部门的映射文件中定义根据部门编号查询部门信息的方法,注意不可以用注解的方式实现!!
<select id="getDeptByDeptno" resultType="Dept" parameterType="Integer">
select * from dept where deptno = #{deptno}
</select>
- 多对一:查询所有部门信息以及每个部门下对应的员工信息(step1-查询部门信息、step2-根据部门信息中的部门编号查询对应的员工信息)
step1: 修改collection标签,也是增加select、colum属性,意义与上同
<!-- 分步查询 -->
<resultMap id="DeptEmpResultMap" type="Dept">
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
<collection property="empList" ofType="Emp" select="com.system.mapper.EmpMapper.empByDeptno" column="deptno" fetchType="eager"></collection>
</resultMap>
<!-- fetchType属性可以设置该步查询是否使用延时加载,eager为不使用延时加载 lazy为使用 -->
<select id="allDeptEmp" resultMap="DeptEmpResultMap">
select * from dept
</select>
step2: 在员工的映射文件下定义根据部门编号查询员工信息的方法
<select id="empByDeptno" resultType="Emp" parameterType="Integer">
select * from emp where deptno=#{deptno}
</select>
colum属性传入多参数
- 在< collection >标签中的colum属性,可以传递多个参数,格式如下所示
- = 的左边为实体类映射的属性名,= 的右边为数据库列字段名
- 注意:在empByDeptno方法中,parameterType属性一定要" parameterType=“java.util.Map” ",否则无法传递多个参数,会报错
<resultMap id="DeptEmpResultMap" type="Dept">
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
<!-- column="{属性名=数据库列名,属性名=数据库列名,...}" -->
<collection property="empList" ofType="Emp" select="com.system.mapper.EmpMapper.empByDeptno" column="deptno"></collection>
</resultMap>
延时加载
延时加载是分步查询的一种“显示策略”,其本质是 “按需加载” 程序用到了才会进行加载,在数据量很大的情况下,大大的提高了性能!!,例如调用上面多对一的例子,调用allDeptEmp方法后,若程序不需要输出员工信息,仅仅输出部门信息,则不会执行step2。只有当程序需要员工信息时,才会执行step2,这样在某些场景下大大提高了性能,节约了资源
- 在全局中配置使用延时加载(在主配置文件中进行配置,默认不开启延时加载),也可以在映射文件中,修改fetch属性,设置单个方法的延时加载策略
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<!--
azyLoadingEnable:true开启延时加载、false不开启
aggressiveLazyLoading:true按照层级延时加载整个对象 false 按需加载。
这两个属性需要同时设置才行!!!!!!!
-->
5)演示:利用Mapper代理增加一个部门信息
Dept dept = new Dept();
dept.setDname("***工作室");
dept.setLoc("南海");
deptMapper.insertDept(dept);
System.out.println("录入部门信息成功:"+dept.getDname()+"-"+dept.getDeptno()+"-"+dept.getLoc());
sqlSession.commit();//使用了事务机制就必须comit才能修改数据库
- 该例子中主键设为自增,并且通过 useGeneratedKeys = true, keyProperty = “deptno” 这两个属性获得了主键自增值(存入了dept对象中,可通过dept.getDeptno()方法获得)。由于该数据库配置了事务机制,因此sqlSession会话对象必须commit才能修改数据库,否则会回滚!!!