Mybatis框架
1.What?
MyBatis是支持普通SQL查询,存储过程和高级映射的优秀持久层框架!以面向对象的方式进行持久化业务,采用XML配置文件,说明映射关系,动态查询,关系映射。
MyBatis可以使用简单的XML或注解用于配置和原始映射,比较有特色的地方是可以定义操作xxx表的sql映射文件(可以对对象的操作采用配置方式进行)。
持久层(ORM):
1)对象--->数据库
2)数据库数据---->对象
2.用于进行持久化业务!
3.HelloWorld:学习应用MyBatis框架的一般步骤(掌握Mybatis框架的体系结构)
1)创建工程
a.创建数据库表:
create table Users(
userId numeric primary key,
userName varchar2(50) not null,
userAge Integer check(userAge > 0)
);
create sequence seq_userid
start with 1
increment by 1
nomaxvalue
nocache;
insert into users values(seq_userid.nextval, 'James', 31);
insert into users values(seq_userid.nextval, 'Jordan',58);
b.创建实体类:
public class User {
private int userId;
private String userName;
private int userAge;
// 添加SET以及GET方法
}
2)导入jar包:mybatis-3.1.1.zip
lib-->mybatis-3.1.1.jar-->add to buildpath
ojdbc6.jar(Oracle11g)
classes12.jar(Oracle10g)
3)创建Mybatis的配置文件conf.xml,在里面配置相关信息(数据源,映射文件,对类名重命名,装载properties资源文件,日志业务定义)
定义在src根目录下
名称一般为conf.xml
标签解读:
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 默认的环境ID,development是开发模式,work为工作模式 -->
<environments default="development">
<!-- 一个SqlSessionFactory对应一个数据库,也就是需要一个environment-->
<!-- 环境ID 和environment的一致 -->
<environment id="development">
<transactionManager type="JDBC" />
<!-- 数据源的配置 有三种内建的数据源类型:
UPOOLED:连接池每次被请求时简单打开和关闭连接
POOLED:这是JDBC连接对象的数据源连接池的实现,用来避免创建新的连接实例时必要的初始连接和认证时间
JNDI:这个数据源的实现是为了使用如Spring或应用服务器这类的容器,容器可以集中或在外部配置数据源
-->
<dataSource type="POOLED">
<!-- 以${xxx }方式表达时,这些值来资源xxx.properties(存在于src目录下)文件,其中xxx就是properties文件中的xxx key
${xxx }表示properties文件中某个key对应的value。
db.properties内容:
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:orcl
username=system
password=oldBOY666
-->
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
或
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521:orcl" />
<property name="username" value="system" />
<property name="password" value="oldBOY666" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml" />
</mappers>
</configuration>
a.<configuration>:作为conf.xml文件的根标记
b.<properties resource="db.properties">:用于引入xx.properties文件
c.<environments default="development">:其中可以有若干个environment,MyBatis可以配置多种环境。这会帮助你将SQL映射应用于多种数据库之中
要记得一个很重要的问题:你可以配置多种环境,但你只能为每个SqlSessionFactory实例选择一个
d.environment:对应一个SqlSessionfactory
e.<transactionManager:事务管理器
f.dataSource:访问数据库的信息写在其中
g.mappers:映射其他文件机构
mapper:
<mapper resource="org/mybatis/example/BlogMapper.xml" />
补充:Mybatis对于映射文件支持,也对指定的POJO类或接口支持——对于其中定义好的业务操作,进行代理实现。
实现主要方式:在相关接口类中的抽象方法头部加注解,也就是说对于实体类相关持久化操作都有对应的注解,比如:@Insert,@Update,@Delete,@Select
<mapper class="com.hd.netstore.dao.IUserDao"/>
Eg:采用注解的方式对于所谓的POJO类或接口进行业务代理实现步骤
1)定义对于某个实体类的业务操作接口
2)定义接口中的实体业务方法
3)在相关方法头部添加注解(其中说明如何实施对应的操作)
4)使用前提:将该接口类信息注册到conf.xml文件中
<mapper class="com.hd.netstore.dao.IUserDao"/>
5)调用或执行业务接口类中的业务行为:
// 谁负责调用?session调用getMapper(xxxIterfaceName.class),从而得到业务接口类的代理对象
// 如何调用?由业务接口代理对象,调用相关业务行为
Eg:
// 通过session调用getMapper(XXXInerfaceName.class),得到业务接口类的对象
IUser userDao = session.getMapper(IUser.class);
// 由业务接口对象调用业务行为
userDao.deleteUser(3);
// 从而得到业务实现
// session.commit();
4)定义操作xxx表(某某实体类)的sql映射文件(xml文件)
定义对于某些类的持久化业务操作的映射文件:
a.位置:和其对应的实体类在同一个包。
b.命名方式:xxxTypeNameMapper.xml
userMapper.xml
c.内容构成:
<?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">
<!-- namespace的取值:是该映射文件去掉后缀之后完全限定名 -->
<mapper namespace="com.hd.netstore.entity.userMapper">
<!-- (1)用select对应查询操作 -->
<!-- id的值是方法名 -->
<!-- parameterType表示参数类别 -->
<!-- resultType表示方法执行之后的结果类型 -->
<select id="getUser" parameterType="int" resultType="com.hd.netstore.entity.User">
<!-- 查询语句:#{id}叫做占位符,相当于sql语句中的? 其中“#”必须要,参数名-->
SELECT * FROM PERSON WHERE userId = #{userId}
</select>
</mapper>
5)调用业务方法实现持久化业务:
Hibernate:
// 定义一个全局Configuration对象
Configuration config = new Configuration();
config.configure("/hibernate.cfg.xml");
// 创建SessionFactory对象,装载所有的对象映射文件
SessionFactory sessionFactory = config.buildSessionFactory();
// 创建session对象,从而为查询遍历做准备
Session session = sessionFactory.openSession();
Mybatis:
String resource = "conf.xml";
//加载mybatis的配置文件(它也加载关联的映射文件)
Reader reader = null;
try {
reader = Resources.getResourceAsReader(resource);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//构建sqlSession的工厂
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
//创建能执行映射文件中sql的sqlSession
SqlSession session = sessionFactory.openSession();
//映射sql的标识字符串
String statement = "com.hd.netstore.entity.userMapper.getUser";
//执行查询返回一个唯一user对象的sql
User user = session.selectOne(statement, 1);
System.out.println(user);
需要参与到持久化业务中的类:
a.Reader:相当于Hibernate框架的Configuration,加载conf.xml文件
Reader reader = null;
reader = Resources.getResourceAsReader("conf.xml");
b.SqlSessionFactory:用于将映射文件解析装载
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
c.SqlSession:相当于链接对象,执行所有增删改的操作。
SqlSession session = sessionFactory.openSession();
Methods:
c-1.selectOne:通过session去进行持久化查询操作时用到的方法
c-2.selectList:通过session去进行持久化查询操作时用到的方法,但是可以查询多个值
c-3.insert:insert通过session去进行持久化添加操作时用到的方法,执行之后会返回一个整数,表示进行添加操作之后,数据库中受此影响的行数。
c-4.update:通过session去进行持久化修改操作时用到的方法
c-5.delete:通过session去进行持久化删除操作时用到的方法
c-6.close():关闭流,撤销资源
c-7.commit():提交事务。
4.补充
修改操作事务,最终提交方式有两种:
1)在获得Session对象引用时,在openSession()方法的括号中,增加值为true的参数,可以实现自动提交
session = sessionFactory.openSession(true);
2)通过session对象的commit()方法,实现业务提交
session.commit();
5.优化功能实现?
5-1.将业务实现时一些重复操作封装起来,便于重用:
1)session的获得
2)关闭session
public class MybatisOrmDbhelper {
public static SqlSession getSession() {
SqlSession session = null;
// 相当于Hibernate框架的Configuration,加载conf.xml文件
Reader reader = null;
try {
reader = Resources.getResourceAsReader("conf.xml");
// 用于将映射文件解析装载
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
// 相当于链接对象,执行所有增删改的操作。
session = sessionFactory.openSession(true);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return session;
}
public static void closeSession(SqlSession session) {
if (session != null) {
session.close();
}
}
}
5-2.优化resultType所使用的全命名格式-->给package.xxxtype ==> target: typeparam,希望可以以typeparam替代package.xxxtype
1)重命名在什么地方?
<typeAlias type="被替代类的全命名信息" alias="类的新的名字"/>
2)怎么实现重命名?
重命名应该让整个系统知道,应该将类的命名参数定义在conf.xml文件中
<typeAliases>
<typeAlias type="被替代类的全命名信息" alias="类的新的名字"/>
...
</typeAliases>
3)使用:在系统中需要表达类别的时候,可以使用alias属性替换原有比较冗长的命名
<typeAliases>
<typeAlias type="com.hd.netstore.entity.User" alias="U"/>
<package name="com.hd.netstore.entity"/>
</typeAliases>
或
<typeAliases>
<typeAlias type="com.hd.netstore.entity.User" alias="U"/>
</typeAliases>
<select id="userList" resultType="com.hd.netstore.entity.User">
<select id="userList" resultType="U">
5-3.解决字段名与实体类属性名不相同的冲突
类的成员变量和对应表的列的好的命名习惯——将类的成员变量的名和表中列的明明保持一致。
如何解决冲突:
Eg:
create table batches(
batchNo varchar2(50) primary key,
batchDirection varchar2(100) not null,
batchEmpCount integer check(batchEmpCount > 0)
);
Teachers(teacherId,teacherName,teacherAge,teacherDirection, batchId)
create table teachers(
teacherId varchar2(50) primary key,
teacherName varchar2(50) not null,
teacherAge integer check(teacherAge>0),
teacherDirection varchar2(100),
batchId varchar2(50) references batches(batchNo)
);
public class Teacher {
private String teaId;
private String teaName;
private int teaAge;
private String teaDirection;
private String batId;
// SET以及GET方法
...
}
解决方案:
1)通过在sql语句中定义别名
<select id="selectOrder" parameterType="int" resultType="_Order">
select order_id id, order_no orderNo,order_price price from orders where order_id=#{id}
</select>
2)通过<resultMap>
<resultMap type="Teacher" id="teacherMap">
<id property="teaId" column="teacherId"></id>
<result property="teaName" column="teacherName"/>
<result property="teaAge" column="teacherAge"/>
<result property="teaDirection" column="teacherDirection"/>
<result property="batId" column="batchId"/>
</resultMap>
<select id="queryTeacherFromResultMapp" parameterType="String" resultMap="teacherMap">
select * from teachers where teacherId = #{teacherId}
</select>
6.关系映射:实现对象级的关联操作
6-1.one to one:一对一关联,期望:通过一对一的关联,实现通过一个对象的属性,直接检索和它一对一关系的对象
Eg:班级 对 老师
1)创建表和类:
reate table batches(
batchNo varchar2(50) primary key,
batchDirection varchar2(100) not null,
batchEmpCount integer check(batchEmpCount > 0)
);
insert into batches values('Batch01','Java', 30);
insert into batches values('Batch02','Html5', 50);
insert into batches values('Batch03','Test', 20);
create table teachers(
teacherId varchar2(50) primary key,
teacherName varchar2(50) not null,
teacherAge integer check(teacherAge>0),
teacherDirection varchar2(100),
batchId varchar2(50) references batches(batchNo)
);
insert into teachers values('SX001','Kobe',39,'Java','Batch01');
insert into teachers values('SX002','KD',29,'HTML5','Batch02');
insert into teachers values('SX003','Jordan',59,'Java','Batch03');
2)创建映射文件:希望通过一个老师,检索他对应的班级对象
a.Method1:结果嵌套
<!-- Method1:
嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集,封装联表查询的数据(去除重复的数据)
-->
<resultMap type="XhTeacher" id="xhTeacherBatchMap">
<!-- Part1:定义所返回值类型相关的类的属性和对应表的列的关系 -->
<id property="teacherId" column="teacherId"></id>
<result property="teacherName" column="teacherName"/>
<result property="teacherAge" column="teacherAge"/>
<result property="teacherDirection" column="teacherDirection"/>
<!-- Part2:说明所关联的对象的特征(包括当前类中的属性名,被关联的类属性和相关列关系) -->
<association property="batch" javaType="com.hd.netstore.entity.Batch">
<id property="batchNo" column="batchNo"></id>
<result property="batchDirection" column="batchDirection"/>
<result property="batchEmpCount" column="batchEmpCount"/>
</association>
</resultMap>
<select id="queryXhTeacher" parameterType="String" resultMap="xhTeacherBatchMap">
select * from teachers t, batches b where t.batchId = b.batchNo and t.teacherId = #{teacherId}
</select>
b.Method2:通过查询嵌套
<!--
Method2:嵌套查询:通过执行另外一个SQL映射语句来返回预期的复杂类型
SELECT * FROM Teacher t WHERE t.teacherid = xxx; // xxx表示一个教师编号
SELECT * FROM Batch b WHERE b.batchId=yyy; //yyy 是上一个查询得到的batchId的值
property:别名(属性名) column:列名
-->
<resultMap type="XhTeacher" id="xhTeacherBatchMap1">
<!-- Part1:定义所返回值类型相关的类的属性和对应表的列的关系 -->
<id property="teacherId" column="teacherId"></id>
<result property="teacherName" column="teacherName"/>
<result property="teacherAge" column="teacherAge"/>
<result property="teacherDirection" column="teacherDirection"/>
<!-- Part2:说明所关联的对象的特征(通过查询嵌套来实现) -->
<!-- column:是xhteacher表中的一个列,该列引用了batches表的batchno -->
<association property="batch" column="batchId" select="queryBatchFromTeacher" ></association>
</resultMap>
<select id="queryXhTeacher1" parameterType="String" resultMap="xhTeacherBatchMap1">
select * from teachers where teacherId = #{teacherId}
</select>
<select id="queryBatchFromTeacher" parameterType="String" resultType="com.hd.netstore.entity.Batch">
select * from batches where batchNo = #{batchNo}
</select>
Class Task:创建两个表——国家首都关系,通过一对一关联,可以实现对象级操作。
6-2.one to many:订单(订单编号,订单总额,订单时间,订单发货地址,卖家) -- 订单构成项(订单构成项编号,订单构成项商品id,订单构成项商品数量,订单编号)
1)创建表和关系:
create table orderspre(
orderId number primary key,
orderTotal numeric check(orderTotal >= 0),
orderTime timestamp,
orderAddress varchar2(200)
);
create sequence seq_orderidpre
start with 1
increment by 1
nomaxvalue;
insert into orderspre values(seq_orderidpre.nextval, 1000, sysdate, 'Shanghai Yanan road 3000');
insert into orderspre values(seq_orderidpre.nextval, 2000, sysdate, 'JiaXing Luli street 100');
insert into orderspre values(seq_orderidpre.nextval, 3000, sysdate, 'Beijing Qianmen road 10');
create table orderIterms(
orderItermId number primary key,
orderId number references orderspre(orderId),
orderItermName varchar2(50) not null,
orderItermCount integer check(orderItermCount>0)
);
create sequence seq_orderItermidpre
start with 1
increment by 1
nomaxvalue;
insert into orderIterms values(seq_orderItermidpre.nextval,1,'Apple8', 5);
insert into orderIterms values(seq_orderItermidpre.nextval,1,'DellBIZ01', 20);
insert into orderIterms values(seq_orderItermidpre.nextval,3,'Lenovo', 30);
2)创建表所对应的实体类:
a.Order.java-->订单表所对应的类型
由于Order是一对多关系一方,所以,在该类的属性中要有一个存储对应多方(订单项)的集合作为属性
private int orderId;
private double orderTotal;
private Timestamp orderTime;
private String orderAddress;
private List<OrderIterm> orderItermList;
注意:在Order类中,记着定义订单项集合属性的SET以及GET方法!!!
b.OrderIterm.java--->订单项对应的类型
private int orderItermId;
private int orderId;
private String orderItermName;
private int orderItermCount;
3)定义一个Order的映射文件,OrderMapper.xml,并将其添加到conf.xml文件:
<mapper resource="com/hd/netstore/entity/OrderMapper.xml"/>
</mapper>
Content shown as bellow:
<resultMap type="com.hd.netstore.entity.Order" id="orderAndItermMap">
<id property="orderId" column="orderId"></id>
<result property="orderTotal" column="orderTotal"/>
<result property="orderTime" column="orderTime"/>
<result property="orderAddress" column="orderAddress"/>
<collection property="orderItermList" ofType="com.hd.netstore.entity.OrderIterm" >
<id property="orderItermId" column="orderItermId"></id>
<result property="orderId" column="orderId"/>
<result property="orderItermName" column="orderItermName"/>
<result property="orderItermCount" column="orderItermCount"/>
</collection>
</resultMap>
<select id="getOrderInfor" parameterType="int" resultMap="orderAndItermMap">
select * from orderspre o, orderIterms s where o.orderId = s.orderId and o.orderId = #{orderId}
</select>
或
<select id="queryOrderAndIterm" parameterType="com.hd.netstore.entity.Order" resultMap="orderAndItermsMap">
<!-- 如果采用自定义类型作为参数,那么要求在SQL语句中的参数要和参数类型的属性相关 -->
select * from orderspre,orderIterms where orderspre.orderId = orderIterms.orderId and orderspre.orderId = #{orderId}
</select>
6-3.many to one:站在many to one的many方来看,任何一个对象,都有一个一对一的一方关联(就是many to one中的one)。
采用one to one的配置方法就可以了:
Eg: orderIterm order
1)在OrderIterm的属性中,添加order属性,并增加SEG和GET方法。
2)给OrderIterm类添加对应的映射文件:OrderItermMapper.xml
内容:
<resultMap type="OrderIterm" id="orderItermAndOrderMap">
<!-- Part1:定义所返回值类型相关的类的属性和对应表的列的关系 -->
<id property="orderItermId" column="orderItermId"></id>
<result property="orderId" column="orderId" />
<result property="orderItermName" column="orderItermName" />
<result property="orderItermCount" column="orderItermCount" />
<!-- Part2:说明所关联的对象的特征(包括当前类中的属性名,被关联的类属性和相关列关系) -->
<association property="order" javaType="com.hd.netstore.entity.Order">
<id property="orderId" column="orderId"></id>
<result property="orderTotal" column="orderTotal" />
<result property="orderTime" column="orderTime" />
<result property="orderAddress" column="orderAddress" />
</association>
</resultMap>
<select id="queryOrderFromOrderIterm" parameterType="int" resultMap="orderItermAndOrderMap">
select * from orderspre od, orderIterms oi where od.orderId = oi.orderId and oi.orderItermId = #{orderItermId}
</select>
3)客户端调用执行
6-4.many to many: Employees Projects
分析出多对多关系之后,要分别创建三个表,其中一个表示关系表(纳入了另外两个表的关键字信息),而且将另外两个表的主键纳入后,形成组合主键。
需要创建的表:员工表、项目表、员工项目关系表
6-4-1.创建表:Employees Projects
1)xhemployees:
create table xhemployees(
empId varchar2(50) primary key,
empName varchar2(50) not null,
empLevel varchar2(50) check (empLevel in ('Level1','Level2','Level3'))
);
2)xhprojects:
create table xhprojects(
projId varchar2(50) primary key,
projName varchar2(200) not null,
projEmpnum integer check(projEmpnum >= 0),
projState integer check(projState=0 or projState = 1)
);
3)xhemp_proj:员工项目关系表(内容包括另外两个表的主键,并由它们构成关系表的组合主键)
create table xhemp_proj (
empId varchar2(50) references xhemployees(empId),
projId varchar2(50) references xhprojects(projid),
createTime timestamp not null
);
commit;
修改表结构,定义复合主键
alter table xhemp_proj add constraints xhep_pk primary key (empId,projId);
4)向三个表中填写数据:
insert into xhemployees values('XHJ001','James','Level1');
insert into xhemployees values('XHJ002','Kobe','Level2');
insert into xhemployees values('XHJ003','Jordan','Level3');
commit;
insert into xhprojects values('PR001','China dragon',50,1);
insert into xhprojects values('PR002','Big data',100,1);
insert into xhprojects values('PR003','West development',80,1);
insert into xhemp_proj values('XHJ001','PR001',sysdate);
insert into xhemp_proj values('XHJ001','PR002',sysdate);
insert into xhemp_proj values('XHJ002','PR001',sysdate);
insert into xhemp_proj values('XHJ003','PR001',sysdate);
commit;
5)定义实体类:
a.定义xhemployees表的实体类-->Employee.java
public class Employee {
private String empId;
private String empName;
private String empLevel;
// 添加属性的SET和GET方法
}
b.定义xhprojects表的实体类-->Project.java
public class Project {
private String projId;
private String projName;
private int projEmpnum;
private int projState;
// 项目中的多个员工,在向目标的属性中,添加一个员工集合,作为属性,然后,给该集合添加SET和GET方法
private List<Employee> employees;
}
c.定义关系对象?建议尝试
6)站在Project的角度来看,它相关员工集合如何实现呢?
a.给Project定义映射文件,在文件中说明关联关系:
映射文件名:ProjectMapper.xml
<resultMap type="com.hd.netstore.entity.Project" id="projectMap">
<id property="projId" column="projId" />
<result property="projName" column="projName" />
<result property="projEmpnum" column="projEmpnum" />
<result property="projState" column="projState" />
</resultMap>
<resultMap id="projectEmployeeMap" type="com.hd.netstore.entity.Project" extends="projectMap">
<collection property="employees" ofType="com.hd.netstore.entity.Employee ">
<id property="empId" column="empId" />
<result property="empName" column="empName" />
<result property="empLevel" column="empLevel" />
</collection>
</resultMap>
项目表 left join 项目员工关系表 on 项目表.项目id = 项目员工关系表.项目id
left join 员工表 on 项目员工关系表.员工id = 员工表.员工id
<select id="queryProjectEmployeeList" parameterType="com.hd.netstore.entity.Project(项目所在的类的全命名格式)" resultMap="projectEmployeeMap" >
select xp.projId,xp.projName,xp.projEmpnum,xp.projState,
xe.empId,xe.empName,xe.empLevel
from
xhprojects xp
left join xhemp_proj ep
on xp.projId=ep.projId
left join xhemployees xe
on ep.empId=xe.empId
<where>
<!--当projId为初始值null,不再使用projId作为查询条件 -->
<if test="projId != null ">xp.projId=#{projId}</if>
</where>
</select>
7.动态SQL查询: MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑。
MyBatis中用于实现动态SQL的元素主要有:
7-1.SQL指令:
1)if
2)choose(when,otherwise)
3)trim
4)where
5)set
6)foreach
8.调用存储过程:procedure,是一种存在于数据库服务器中的对象,一旦创建,就可以多次使用,执行速度快,效率高。
它可以通过输出参数获得多个结果,结果相当于返回值。
8-1.存储过程
Synax:
注意:在定义procedure时候,参数区中的变量类型不要具体化。
在存储过程中SQL语句中,给变量赋值的手法——采用select into xxxvaribaleName ...
在存储过程中给变量赋值,可以采用直接赋值:
xxxvaribaleName := value;
create or replace procedure proc_xxx(
paramName1 in | out | in out Type,
paramName2 in | out | in out Type,
...
)
as
declare ...
begin
statement...
Exception
when condition then
... ;
end proc_xxx;
存储过程创建语法:
create or replace procedure 存储过程名(param1 in type,param2 out type)
as
变量1 类型(值范围);
变量2 类型(值范围);
Begin
Select count(*) into 变量1 from 表A where列名=param1;
If (判断条件) then
Select 列名 into 变量2 from 表A where列名=param1;
Dbms_output.Put_line(‘打印信息’);
Elsif (判断条件) then
Dbms_output.Put_line(‘打印信息’);
Else
Raise 异常名(NO_DATA_FOUND);
End if;
Exception
When others then
Rollback;
End;
Eg:
create or replace procedure proc_query_userage(
usId in Integer,
uName in Varchar2,
age out integer
)
as
rowcount integer;
begin
select count(*) into rowcount from users where userId = usId and userName = uName;
if (rowcount > 0) then
select userAge into age from users where userId = usId and userName = uName;
else
age := rowcount;
end if;
Exception
when others then
rollback;
end;
在Oracle console控制台调用存储过程的语法:在调用时,给予输入参数值的时候,把赋值放在数据类型之后
declare
usId Integer :=9;
uName Varchar2(50) :='YaoMing';
age integer;
begin
proc_query_userage(usId,uName,age);
dbms_output.put_line( age);
end;
8-2. 在Mybatis中调用存储过程
往往在一个映射文件中有那么一些增删该查的操作,存储过程的调用就体现在它们里面。
支持的JDBC类型
为了未来的参考,MyBatis通过包含的jdbcType枚举型,支持下面的JDBC类型。
BIT
FLOAT
CHAR
TIMESTAMP
OTHER
UNDEFINED
TINYINT
REAL
VARCHAR
BINARY
BLOB
NVARCHAR
SMALLINT
DOUBLE
LONGVARCHAR
VARBINARY
CLOB
NCHAR
INTEGER
NUMERIC
DATE
LONGVARBINARY
BOOLEAN
NCLOB
BIGINT
DECIMAL
TIME
NULL
CURSOR
1)在映射文件中,调用存储过程语法:
在某个业务操作中,调用相关的存储过程,调用时,调用语法:call proc_xxxx(?,...),业务操作标签的statementType="CALLABLE",返回值和输入值是同一个参数,参数
列表是Map集合,该集合的定义采用parameterMap标签实现,其中type取值为java.util.Map,id表示该参数集合的名字,其中的子标签parameter用于说明该存储过程的参数
要求。
参数的定义:<parameter property="usId" mode="IN" jdbcType="INTEGER" />
property:是参数的名字
mode:是参数的类别(取值范围是:IN,OUT,INOUT)
jdbcType:是指定参数的数据类型,而且是指Mybatis中认可的类别。
<!-- 调用存储过程 -->
<parameterMap type="java.util.Map" id="getAgeMap">
<parameter property="usId" mode="IN" jdbcType="INTEGER" />
<parameter property="usName" mode="IN" jdbcType="VARCHAR" />
<parameter property="usAge" mode="OUT" jdbcType="INTEGER" />
</parameterMap>
<select id="queryUserAge" statementType="CALLABLE" parameterMap="getAgeMap">
call proc_query_userage(?,?,?)
</select>
2)java程序调用映射文件中的某个业务行为(该行为中有调用存储过程):
a.准备一个Map对象,用于存储存储过程需要的参数,参数的名字要和定义在映射文件中的参数名一致。添加到Map集合的格式如下:
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("usId", 1);
paramMap.put("usName", "YaoMing");
paramMap.put("usAge", 0);
b.将Map参数集合对象作为参数提交给相关业务方法
session.selectOne(statement, paramMap);
c.Map集合对象负责将存储过程输出参数的值取出:
paramMap.get("输出参数的名字")
Class Task: 定义一个模拟登录的存储过程,要求提供用户名和密码,经过查询,通过返回值说明验证的结果,在java程序中调用存储过程,获得验证的结果。
9.一级二级缓存:
hibernate 的一级缓存 session 二级缓存 sessionFactory
mybats : 一级缓存:session 二级缓存是对应一个XxxMapper(某个类的映射文件)
一级缓存: 当session多次 调用select 时,如果没有进行修改(更新,修改,删除),则已经持久化到缓存中的对象是同一个
当session主动调用clearCache()方法时,会清空一级缓存
二级缓存: 使用的时候,在配置文件中添加<cache/> 实体类必须实现序列化接口
添加一个二级缓存的配置文件
10.Spring和SpringMvc的整合
11.Spring和Mybatis的整合
log4j
%d是时间, %l;路径 [%t]线程 P:消息级别 c 类名 m 消息 N 换行
1.What?
MyBatis是支持普通SQL查询,存储过程和高级映射的优秀持久层框架!以面向对象的方式进行持久化业务,采用XML配置文件,说明映射关系,动态查询,关系映射。
MyBatis可以使用简单的XML或注解用于配置和原始映射,比较有特色的地方是可以定义操作xxx表的sql映射文件(可以对对象的操作采用配置方式进行)。
持久层(ORM):
1)对象--->数据库
2)数据库数据---->对象
2.用于进行持久化业务!
3.HelloWorld:学习应用MyBatis框架的一般步骤(掌握Mybatis框架的体系结构)
1)创建工程
a.创建数据库表:
create table Users(
userId numeric primary key,
userName varchar2(50) not null,
userAge Integer check(userAge > 0)
);
create sequence seq_userid
start with 1
increment by 1
nomaxvalue
nocache;
insert into users values(seq_userid.nextval, 'James', 31);
insert into users values(seq_userid.nextval, 'Jordan',58);
b.创建实体类:
public class User {
private int userId;
private String userName;
private int userAge;
// 添加SET以及GET方法
}
2)导入jar包:mybatis-3.1.1.zip
lib-->mybatis-3.1.1.jar-->add to buildpath
ojdbc6.jar(Oracle11g)
classes12.jar(Oracle10g)
3)创建Mybatis的配置文件conf.xml,在里面配置相关信息(数据源,映射文件,对类名重命名,装载properties资源文件,日志业务定义)
定义在src根目录下
名称一般为conf.xml
标签解读:
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 默认的环境ID,development是开发模式,work为工作模式 -->
<environments default="development">
<!-- 一个SqlSessionFactory对应一个数据库,也就是需要一个environment-->
<!-- 环境ID 和environment的一致 -->
<environment id="development">
<transactionManager type="JDBC" />
<!-- 数据源的配置 有三种内建的数据源类型:
UPOOLED:连接池每次被请求时简单打开和关闭连接
POOLED:这是JDBC连接对象的数据源连接池的实现,用来避免创建新的连接实例时必要的初始连接和认证时间
JNDI:这个数据源的实现是为了使用如Spring或应用服务器这类的容器,容器可以集中或在外部配置数据源
-->
<dataSource type="POOLED">
<!-- 以${xxx }方式表达时,这些值来资源xxx.properties(存在于src目录下)文件,其中xxx就是properties文件中的xxx key
${xxx }表示properties文件中某个key对应的value。
db.properties内容:
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:orcl
username=system
password=oldBOY666
-->
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
或
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521:orcl" />
<property name="username" value="system" />
<property name="password" value="oldBOY666" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml" />
</mappers>
</configuration>
a.<configuration>:作为conf.xml文件的根标记
b.<properties resource="db.properties">:用于引入xx.properties文件
c.<environments default="development">:其中可以有若干个environment,MyBatis可以配置多种环境。这会帮助你将SQL映射应用于多种数据库之中
要记得一个很重要的问题:你可以配置多种环境,但你只能为每个SqlSessionFactory实例选择一个
d.environment:对应一个SqlSessionfactory
e.<transactionManager:事务管理器
f.dataSource:访问数据库的信息写在其中
g.mappers:映射其他文件机构
mapper:
<mapper resource="org/mybatis/example/BlogMapper.xml" />
补充:Mybatis对于映射文件支持,也对指定的POJO类或接口支持——对于其中定义好的业务操作,进行代理实现。
实现主要方式:在相关接口类中的抽象方法头部加注解,也就是说对于实体类相关持久化操作都有对应的注解,比如:@Insert,@Update,@Delete,@Select
<mapper class="com.hd.netstore.dao.IUserDao"/>
Eg:采用注解的方式对于所谓的POJO类或接口进行业务代理实现步骤
1)定义对于某个实体类的业务操作接口
2)定义接口中的实体业务方法
3)在相关方法头部添加注解(其中说明如何实施对应的操作)
4)使用前提:将该接口类信息注册到conf.xml文件中
<mapper class="com.hd.netstore.dao.IUserDao"/>
5)调用或执行业务接口类中的业务行为:
// 谁负责调用?session调用getMapper(xxxIterfaceName.class),从而得到业务接口类的代理对象
// 如何调用?由业务接口代理对象,调用相关业务行为
Eg:
// 通过session调用getMapper(XXXInerfaceName.class),得到业务接口类的对象
IUser userDao = session.getMapper(IUser.class);
// 由业务接口对象调用业务行为
userDao.deleteUser(3);
// 从而得到业务实现
// session.commit();
4)定义操作xxx表(某某实体类)的sql映射文件(xml文件)
定义对于某些类的持久化业务操作的映射文件:
a.位置:和其对应的实体类在同一个包。
b.命名方式:xxxTypeNameMapper.xml
userMapper.xml
c.内容构成:
<?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">
<!-- namespace的取值:是该映射文件去掉后缀之后完全限定名 -->
<mapper namespace="com.hd.netstore.entity.userMapper">
<!-- (1)用select对应查询操作 -->
<!-- id的值是方法名 -->
<!-- parameterType表示参数类别 -->
<!-- resultType表示方法执行之后的结果类型 -->
<select id="getUser" parameterType="int" resultType="com.hd.netstore.entity.User">
<!-- 查询语句:#{id}叫做占位符,相当于sql语句中的? 其中“#”必须要,参数名-->
SELECT * FROM PERSON WHERE userId = #{userId}
</select>
</mapper>
5)调用业务方法实现持久化业务:
Hibernate:
// 定义一个全局Configuration对象
Configuration config = new Configuration();
config.configure("/hibernate.cfg.xml");
// 创建SessionFactory对象,装载所有的对象映射文件
SessionFactory sessionFactory = config.buildSessionFactory();
// 创建session对象,从而为查询遍历做准备
Session session = sessionFactory.openSession();
Mybatis:
String resource = "conf.xml";
//加载mybatis的配置文件(它也加载关联的映射文件)
Reader reader = null;
try {
reader = Resources.getResourceAsReader(resource);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//构建sqlSession的工厂
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
//创建能执行映射文件中sql的sqlSession
SqlSession session = sessionFactory.openSession();
//映射sql的标识字符串
String statement = "com.hd.netstore.entity.userMapper.getUser";
//执行查询返回一个唯一user对象的sql
User user = session.selectOne(statement, 1);
System.out.println(user);
需要参与到持久化业务中的类:
a.Reader:相当于Hibernate框架的Configuration,加载conf.xml文件
Reader reader = null;
reader = Resources.getResourceAsReader("conf.xml");
b.SqlSessionFactory:用于将映射文件解析装载
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
c.SqlSession:相当于链接对象,执行所有增删改的操作。
SqlSession session = sessionFactory.openSession();
Methods:
c-1.selectOne:通过session去进行持久化查询操作时用到的方法
c-2.selectList:通过session去进行持久化查询操作时用到的方法,但是可以查询多个值
c-3.insert:insert通过session去进行持久化添加操作时用到的方法,执行之后会返回一个整数,表示进行添加操作之后,数据库中受此影响的行数。
c-4.update:通过session去进行持久化修改操作时用到的方法
c-5.delete:通过session去进行持久化删除操作时用到的方法
c-6.close():关闭流,撤销资源
c-7.commit():提交事务。
4.补充
修改操作事务,最终提交方式有两种:
1)在获得Session对象引用时,在openSession()方法的括号中,增加值为true的参数,可以实现自动提交
session = sessionFactory.openSession(true);
2)通过session对象的commit()方法,实现业务提交
session.commit();
5.优化功能实现?
5-1.将业务实现时一些重复操作封装起来,便于重用:
1)session的获得
2)关闭session
public class MybatisOrmDbhelper {
public static SqlSession getSession() {
SqlSession session = null;
// 相当于Hibernate框架的Configuration,加载conf.xml文件
Reader reader = null;
try {
reader = Resources.getResourceAsReader("conf.xml");
// 用于将映射文件解析装载
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
// 相当于链接对象,执行所有增删改的操作。
session = sessionFactory.openSession(true);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return session;
}
public static void closeSession(SqlSession session) {
if (session != null) {
session.close();
}
}
}
5-2.优化resultType所使用的全命名格式-->给package.xxxtype ==> target: typeparam,希望可以以typeparam替代package.xxxtype
1)重命名在什么地方?
<typeAlias type="被替代类的全命名信息" alias="类的新的名字"/>
2)怎么实现重命名?
重命名应该让整个系统知道,应该将类的命名参数定义在conf.xml文件中
<typeAliases>
<typeAlias type="被替代类的全命名信息" alias="类的新的名字"/>
...
</typeAliases>
3)使用:在系统中需要表达类别的时候,可以使用alias属性替换原有比较冗长的命名
<typeAliases>
<typeAlias type="com.hd.netstore.entity.User" alias="U"/>
<package name="com.hd.netstore.entity"/>
</typeAliases>
或
<typeAliases>
<typeAlias type="com.hd.netstore.entity.User" alias="U"/>
</typeAliases>
<select id="userList" resultType="com.hd.netstore.entity.User">
<select id="userList" resultType="U">
5-3.解决字段名与实体类属性名不相同的冲突
类的成员变量和对应表的列的好的命名习惯——将类的成员变量的名和表中列的明明保持一致。
如何解决冲突:
Eg:
create table batches(
batchNo varchar2(50) primary key,
batchDirection varchar2(100) not null,
batchEmpCount integer check(batchEmpCount > 0)
);
Teachers(teacherId,teacherName,teacherAge,teacherDirection, batchId)
create table teachers(
teacherId varchar2(50) primary key,
teacherName varchar2(50) not null,
teacherAge integer check(teacherAge>0),
teacherDirection varchar2(100),
batchId varchar2(50) references batches(batchNo)
);
public class Teacher {
private String teaId;
private String teaName;
private int teaAge;
private String teaDirection;
private String batId;
// SET以及GET方法
...
}
解决方案:
1)通过在sql语句中定义别名
<select id="selectOrder" parameterType="int" resultType="_Order">
select order_id id, order_no orderNo,order_price price from orders where order_id=#{id}
</select>
2)通过<resultMap>
<resultMap type="Teacher" id="teacherMap">
<id property="teaId" column="teacherId"></id>
<result property="teaName" column="teacherName"/>
<result property="teaAge" column="teacherAge"/>
<result property="teaDirection" column="teacherDirection"/>
<result property="batId" column="batchId"/>
</resultMap>
<select id="queryTeacherFromResultMapp" parameterType="String" resultMap="teacherMap">
select * from teachers where teacherId = #{teacherId}
</select>
6.关系映射:实现对象级的关联操作
6-1.one to one:一对一关联,期望:通过一对一的关联,实现通过一个对象的属性,直接检索和它一对一关系的对象
Eg:班级 对 老师
1)创建表和类:
reate table batches(
batchNo varchar2(50) primary key,
batchDirection varchar2(100) not null,
batchEmpCount integer check(batchEmpCount > 0)
);
insert into batches values('Batch01','Java', 30);
insert into batches values('Batch02','Html5', 50);
insert into batches values('Batch03','Test', 20);
create table teachers(
teacherId varchar2(50) primary key,
teacherName varchar2(50) not null,
teacherAge integer check(teacherAge>0),
teacherDirection varchar2(100),
batchId varchar2(50) references batches(batchNo)
);
insert into teachers values('SX001','Kobe',39,'Java','Batch01');
insert into teachers values('SX002','KD',29,'HTML5','Batch02');
insert into teachers values('SX003','Jordan',59,'Java','Batch03');
2)创建映射文件:希望通过一个老师,检索他对应的班级对象
a.Method1:结果嵌套
<!-- Method1:
嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集,封装联表查询的数据(去除重复的数据)
-->
<resultMap type="XhTeacher" id="xhTeacherBatchMap">
<!-- Part1:定义所返回值类型相关的类的属性和对应表的列的关系 -->
<id property="teacherId" column="teacherId"></id>
<result property="teacherName" column="teacherName"/>
<result property="teacherAge" column="teacherAge"/>
<result property="teacherDirection" column="teacherDirection"/>
<!-- Part2:说明所关联的对象的特征(包括当前类中的属性名,被关联的类属性和相关列关系) -->
<association property="batch" javaType="com.hd.netstore.entity.Batch">
<id property="batchNo" column="batchNo"></id>
<result property="batchDirection" column="batchDirection"/>
<result property="batchEmpCount" column="batchEmpCount"/>
</association>
</resultMap>
<select id="queryXhTeacher" parameterType="String" resultMap="xhTeacherBatchMap">
select * from teachers t, batches b where t.batchId = b.batchNo and t.teacherId = #{teacherId}
</select>
b.Method2:通过查询嵌套
<!--
Method2:嵌套查询:通过执行另外一个SQL映射语句来返回预期的复杂类型
SELECT * FROM Teacher t WHERE t.teacherid = xxx; // xxx表示一个教师编号
SELECT * FROM Batch b WHERE b.batchId=yyy; //yyy 是上一个查询得到的batchId的值
property:别名(属性名) column:列名
-->
<resultMap type="XhTeacher" id="xhTeacherBatchMap1">
<!-- Part1:定义所返回值类型相关的类的属性和对应表的列的关系 -->
<id property="teacherId" column="teacherId"></id>
<result property="teacherName" column="teacherName"/>
<result property="teacherAge" column="teacherAge"/>
<result property="teacherDirection" column="teacherDirection"/>
<!-- Part2:说明所关联的对象的特征(通过查询嵌套来实现) -->
<!-- column:是xhteacher表中的一个列,该列引用了batches表的batchno -->
<association property="batch" column="batchId" select="queryBatchFromTeacher" ></association>
</resultMap>
<select id="queryXhTeacher1" parameterType="String" resultMap="xhTeacherBatchMap1">
select * from teachers where teacherId = #{teacherId}
</select>
<select id="queryBatchFromTeacher" parameterType="String" resultType="com.hd.netstore.entity.Batch">
select * from batches where batchNo = #{batchNo}
</select>
Class Task:创建两个表——国家首都关系,通过一对一关联,可以实现对象级操作。
6-2.one to many:订单(订单编号,订单总额,订单时间,订单发货地址,卖家) -- 订单构成项(订单构成项编号,订单构成项商品id,订单构成项商品数量,订单编号)
1)创建表和关系:
create table orderspre(
orderId number primary key,
orderTotal numeric check(orderTotal >= 0),
orderTime timestamp,
orderAddress varchar2(200)
);
create sequence seq_orderidpre
start with 1
increment by 1
nomaxvalue;
insert into orderspre values(seq_orderidpre.nextval, 1000, sysdate, 'Shanghai Yanan road 3000');
insert into orderspre values(seq_orderidpre.nextval, 2000, sysdate, 'JiaXing Luli street 100');
insert into orderspre values(seq_orderidpre.nextval, 3000, sysdate, 'Beijing Qianmen road 10');
create table orderIterms(
orderItermId number primary key,
orderId number references orderspre(orderId),
orderItermName varchar2(50) not null,
orderItermCount integer check(orderItermCount>0)
);
create sequence seq_orderItermidpre
start with 1
increment by 1
nomaxvalue;
insert into orderIterms values(seq_orderItermidpre.nextval,1,'Apple8', 5);
insert into orderIterms values(seq_orderItermidpre.nextval,1,'DellBIZ01', 20);
insert into orderIterms values(seq_orderItermidpre.nextval,3,'Lenovo', 30);
2)创建表所对应的实体类:
a.Order.java-->订单表所对应的类型
由于Order是一对多关系一方,所以,在该类的属性中要有一个存储对应多方(订单项)的集合作为属性
private int orderId;
private double orderTotal;
private Timestamp orderTime;
private String orderAddress;
private List<OrderIterm> orderItermList;
注意:在Order类中,记着定义订单项集合属性的SET以及GET方法!!!
b.OrderIterm.java--->订单项对应的类型
private int orderItermId;
private int orderId;
private String orderItermName;
private int orderItermCount;
3)定义一个Order的映射文件,OrderMapper.xml,并将其添加到conf.xml文件:
<mapper resource="com/hd/netstore/entity/OrderMapper.xml"/>
</mapper>
Content shown as bellow:
<resultMap type="com.hd.netstore.entity.Order" id="orderAndItermMap">
<id property="orderId" column="orderId"></id>
<result property="orderTotal" column="orderTotal"/>
<result property="orderTime" column="orderTime"/>
<result property="orderAddress" column="orderAddress"/>
<collection property="orderItermList" ofType="com.hd.netstore.entity.OrderIterm" >
<id property="orderItermId" column="orderItermId"></id>
<result property="orderId" column="orderId"/>
<result property="orderItermName" column="orderItermName"/>
<result property="orderItermCount" column="orderItermCount"/>
</collection>
</resultMap>
<select id="getOrderInfor" parameterType="int" resultMap="orderAndItermMap">
select * from orderspre o, orderIterms s where o.orderId = s.orderId and o.orderId = #{orderId}
</select>
或
<select id="queryOrderAndIterm" parameterType="com.hd.netstore.entity.Order" resultMap="orderAndItermsMap">
<!-- 如果采用自定义类型作为参数,那么要求在SQL语句中的参数要和参数类型的属性相关 -->
select * from orderspre,orderIterms where orderspre.orderId = orderIterms.orderId and orderspre.orderId = #{orderId}
</select>
6-3.many to one:站在many to one的many方来看,任何一个对象,都有一个一对一的一方关联(就是many to one中的one)。
采用one to one的配置方法就可以了:
Eg: orderIterm order
1)在OrderIterm的属性中,添加order属性,并增加SEG和GET方法。
2)给OrderIterm类添加对应的映射文件:OrderItermMapper.xml
内容:
<resultMap type="OrderIterm" id="orderItermAndOrderMap">
<!-- Part1:定义所返回值类型相关的类的属性和对应表的列的关系 -->
<id property="orderItermId" column="orderItermId"></id>
<result property="orderId" column="orderId" />
<result property="orderItermName" column="orderItermName" />
<result property="orderItermCount" column="orderItermCount" />
<!-- Part2:说明所关联的对象的特征(包括当前类中的属性名,被关联的类属性和相关列关系) -->
<association property="order" javaType="com.hd.netstore.entity.Order">
<id property="orderId" column="orderId"></id>
<result property="orderTotal" column="orderTotal" />
<result property="orderTime" column="orderTime" />
<result property="orderAddress" column="orderAddress" />
</association>
</resultMap>
<select id="queryOrderFromOrderIterm" parameterType="int" resultMap="orderItermAndOrderMap">
select * from orderspre od, orderIterms oi where od.orderId = oi.orderId and oi.orderItermId = #{orderItermId}
</select>
3)客户端调用执行
6-4.many to many: Employees Projects
分析出多对多关系之后,要分别创建三个表,其中一个表示关系表(纳入了另外两个表的关键字信息),而且将另外两个表的主键纳入后,形成组合主键。
需要创建的表:员工表、项目表、员工项目关系表
6-4-1.创建表:Employees Projects
1)xhemployees:
create table xhemployees(
empId varchar2(50) primary key,
empName varchar2(50) not null,
empLevel varchar2(50) check (empLevel in ('Level1','Level2','Level3'))
);
2)xhprojects:
create table xhprojects(
projId varchar2(50) primary key,
projName varchar2(200) not null,
projEmpnum integer check(projEmpnum >= 0),
projState integer check(projState=0 or projState = 1)
);
3)xhemp_proj:员工项目关系表(内容包括另外两个表的主键,并由它们构成关系表的组合主键)
create table xhemp_proj (
empId varchar2(50) references xhemployees(empId),
projId varchar2(50) references xhprojects(projid),
createTime timestamp not null
);
commit;
修改表结构,定义复合主键
alter table xhemp_proj add constraints xhep_pk primary key (empId,projId);
4)向三个表中填写数据:
insert into xhemployees values('XHJ001','James','Level1');
insert into xhemployees values('XHJ002','Kobe','Level2');
insert into xhemployees values('XHJ003','Jordan','Level3');
commit;
insert into xhprojects values('PR001','China dragon',50,1);
insert into xhprojects values('PR002','Big data',100,1);
insert into xhprojects values('PR003','West development',80,1);
insert into xhemp_proj values('XHJ001','PR001',sysdate);
insert into xhemp_proj values('XHJ001','PR002',sysdate);
insert into xhemp_proj values('XHJ002','PR001',sysdate);
insert into xhemp_proj values('XHJ003','PR001',sysdate);
commit;
5)定义实体类:
a.定义xhemployees表的实体类-->Employee.java
public class Employee {
private String empId;
private String empName;
private String empLevel;
// 添加属性的SET和GET方法
}
b.定义xhprojects表的实体类-->Project.java
public class Project {
private String projId;
private String projName;
private int projEmpnum;
private int projState;
// 项目中的多个员工,在向目标的属性中,添加一个员工集合,作为属性,然后,给该集合添加SET和GET方法
private List<Employee> employees;
}
c.定义关系对象?建议尝试
6)站在Project的角度来看,它相关员工集合如何实现呢?
a.给Project定义映射文件,在文件中说明关联关系:
映射文件名:ProjectMapper.xml
<resultMap type="com.hd.netstore.entity.Project" id="projectMap">
<id property="projId" column="projId" />
<result property="projName" column="projName" />
<result property="projEmpnum" column="projEmpnum" />
<result property="projState" column="projState" />
</resultMap>
<resultMap id="projectEmployeeMap" type="com.hd.netstore.entity.Project" extends="projectMap">
<collection property="employees" ofType="com.hd.netstore.entity.Employee ">
<id property="empId" column="empId" />
<result property="empName" column="empName" />
<result property="empLevel" column="empLevel" />
</collection>
</resultMap>
项目表 left join 项目员工关系表 on 项目表.项目id = 项目员工关系表.项目id
left join 员工表 on 项目员工关系表.员工id = 员工表.员工id
<select id="queryProjectEmployeeList" parameterType="com.hd.netstore.entity.Project(项目所在的类的全命名格式)" resultMap="projectEmployeeMap" >
select xp.projId,xp.projName,xp.projEmpnum,xp.projState,
xe.empId,xe.empName,xe.empLevel
from
xhprojects xp
left join xhemp_proj ep
on xp.projId=ep.projId
left join xhemployees xe
on ep.empId=xe.empId
<where>
<!--当projId为初始值null,不再使用projId作为查询条件 -->
<if test="projId != null ">xp.projId=#{projId}</if>
</where>
</select>
7.动态SQL查询: MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑。
MyBatis中用于实现动态SQL的元素主要有:
7-1.SQL指令:
1)if
2)choose(when,otherwise)
3)trim
4)where
5)set
6)foreach
8.调用存储过程:procedure,是一种存在于数据库服务器中的对象,一旦创建,就可以多次使用,执行速度快,效率高。
它可以通过输出参数获得多个结果,结果相当于返回值。
8-1.存储过程
Synax:
注意:在定义procedure时候,参数区中的变量类型不要具体化。
在存储过程中SQL语句中,给变量赋值的手法——采用select into xxxvaribaleName ...
在存储过程中给变量赋值,可以采用直接赋值:
xxxvaribaleName := value;
create or replace procedure proc_xxx(
paramName1 in | out | in out Type,
paramName2 in | out | in out Type,
...
)
as
declare ...
begin
statement...
Exception
when condition then
... ;
end proc_xxx;
存储过程创建语法:
create or replace procedure 存储过程名(param1 in type,param2 out type)
as
变量1 类型(值范围);
变量2 类型(值范围);
Begin
Select count(*) into 变量1 from 表A where列名=param1;
If (判断条件) then
Select 列名 into 变量2 from 表A where列名=param1;
Dbms_output.Put_line(‘打印信息’);
Elsif (判断条件) then
Dbms_output.Put_line(‘打印信息’);
Else
Raise 异常名(NO_DATA_FOUND);
End if;
Exception
When others then
Rollback;
End;
Eg:
create or replace procedure proc_query_userage(
usId in Integer,
uName in Varchar2,
age out integer
)
as
rowcount integer;
begin
select count(*) into rowcount from users where userId = usId and userName = uName;
if (rowcount > 0) then
select userAge into age from users where userId = usId and userName = uName;
else
age := rowcount;
end if;
Exception
when others then
rollback;
end;
在Oracle console控制台调用存储过程的语法:在调用时,给予输入参数值的时候,把赋值放在数据类型之后
declare
usId Integer :=9;
uName Varchar2(50) :='YaoMing';
age integer;
begin
proc_query_userage(usId,uName,age);
dbms_output.put_line( age);
end;
8-2. 在Mybatis中调用存储过程
往往在一个映射文件中有那么一些增删该查的操作,存储过程的调用就体现在它们里面。
支持的JDBC类型
为了未来的参考,MyBatis通过包含的jdbcType枚举型,支持下面的JDBC类型。
BIT
FLOAT
CHAR
TIMESTAMP
OTHER
UNDEFINED
TINYINT
REAL
VARCHAR
BINARY
BLOB
NVARCHAR
SMALLINT
DOUBLE
LONGVARCHAR
VARBINARY
CLOB
NCHAR
INTEGER
NUMERIC
DATE
LONGVARBINARY
BOOLEAN
NCLOB
BIGINT
DECIMAL
TIME
NULL
CURSOR
1)在映射文件中,调用存储过程语法:
在某个业务操作中,调用相关的存储过程,调用时,调用语法:call proc_xxxx(?,...),业务操作标签的statementType="CALLABLE",返回值和输入值是同一个参数,参数
列表是Map集合,该集合的定义采用parameterMap标签实现,其中type取值为java.util.Map,id表示该参数集合的名字,其中的子标签parameter用于说明该存储过程的参数
要求。
参数的定义:<parameter property="usId" mode="IN" jdbcType="INTEGER" />
property:是参数的名字
mode:是参数的类别(取值范围是:IN,OUT,INOUT)
jdbcType:是指定参数的数据类型,而且是指Mybatis中认可的类别。
<!-- 调用存储过程 -->
<parameterMap type="java.util.Map" id="getAgeMap">
<parameter property="usId" mode="IN" jdbcType="INTEGER" />
<parameter property="usName" mode="IN" jdbcType="VARCHAR" />
<parameter property="usAge" mode="OUT" jdbcType="INTEGER" />
</parameterMap>
<select id="queryUserAge" statementType="CALLABLE" parameterMap="getAgeMap">
call proc_query_userage(?,?,?)
</select>
2)java程序调用映射文件中的某个业务行为(该行为中有调用存储过程):
a.准备一个Map对象,用于存储存储过程需要的参数,参数的名字要和定义在映射文件中的参数名一致。添加到Map集合的格式如下:
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("usId", 1);
paramMap.put("usName", "YaoMing");
paramMap.put("usAge", 0);
b.将Map参数集合对象作为参数提交给相关业务方法
session.selectOne(statement, paramMap);
c.Map集合对象负责将存储过程输出参数的值取出:
paramMap.get("输出参数的名字")
Class Task: 定义一个模拟登录的存储过程,要求提供用户名和密码,经过查询,通过返回值说明验证的结果,在java程序中调用存储过程,获得验证的结果。
9.一级二级缓存:
hibernate 的一级缓存 session 二级缓存 sessionFactory
mybats : 一级缓存:session 二级缓存是对应一个XxxMapper(某个类的映射文件)
一级缓存: 当session多次 调用select 时,如果没有进行修改(更新,修改,删除),则已经持久化到缓存中的对象是同一个
当session主动调用clearCache()方法时,会清空一级缓存
二级缓存: 使用的时候,在配置文件中添加<cache/> 实体类必须实现序列化接口
添加一个二级缓存的配置文件
10.Spring和SpringMvc的整合
11.Spring和Mybatis的整合
log4j
%d是时间, %l;路径 [%t]线程 P:消息级别 c 类名 m 消息 N 换行