Mybatis基础知识
0.1mvc
M是指业务模型model,V是指用户界面view,C则是控制器controller,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式(pc+手机+ipad)
model:(实体类+dao+业务) 对数据和功能封装的实体类+业务类+dao类
view:(HTML+CSS+JS+jsp) 页面相关的技术(获取客户的数据+结果数据的展示)
controller:(servlet) 选择页面跳转
mvc是一种通用的软件设计模式
- 优点
1 降低耦合度:视图层和业务层分离
2 提高代码的复用性:
3 便于分工开发
4 维护性高
- 缺点
1 调试困难(单元测试困难)
2 增加了程序的复杂度
3 不适合中小型项目
0.2 web三层架构
对javaweb项目结构的按业务应用进行划分:
表示层:ui:user interface
用于显示数据和接收用户输入的数据(jsp+servlet)
业务逻辑层:BLL:Business Logic Layer
负责关键业务的处理和数据的传递(service)
持久层:DAL:data access layer
主要负责对数据库的直接访问(dao+entity)
1 概念
mybatis:又名ibatis
支持定制化 SQL、存储过程以及高级映射的 由Apache组织维护的开源的持久层框架
简单认知:替代jdbc
jdbc的四宗罪
* jdbc缺点:
* 1 频繁的创建和销毁连接:数据库服务器压力大
* 2 java代码和sql代码混合在一起:学习成本高 可读性差 维护麻烦
* 3 给占位符赋值时:麻烦易错
* 4 处理结果集时:麻烦易错
mybatis解决jdbc的缺点
1 mybatis自带连接池 对连接的创建、初始化、分配、维护、销毁进行管理
2 mybatis是面向配置的编程:把sql语句从java代码中分离::sql映射文件
3 mybatis可以实现java对象与表记录的自动映射:java对象的属性可以自动sql语句中的对应占位符赋值
4 mybatis可以实现表记录与java对象的自动映射:可以实现数据行数据自动解析为java对象
mybatis的特点
* 1 轻量级:
* 2 支持动态sql:通过标签实现sql的拼接
* 3 面向配置的编程:核心配置文件(mybatis的基本设置)+sql映射文件(同一个表的所有sql标签)
* 4 sql语句与java代码的完全分离:单独的文件:sql映射文件存储所有的sql语句
* 5 良好支持复杂的数据映射:支持原生态的sql 比较适用于功能多变 模块不确定的项目
* 6 简单易学 容易上手
orm
orm:object relational mapping 对象关系映射
java中对象的属性与sql表中列的对应关系:
解决的问题:给占位符赋值和解析结果集
mybatis和hibernate的区别
都是持久层框架:替代jdbc 核心都是ORM
1 mybatis简单易学 学习成本低
hibernate比较复杂 学习成本高
2 hibernate是完全的orm框架:不存在sql 对jdbc进行了完全的封装
mybatis不是完全的orm框架 需要手写sql
3 hibernate比较适用于传统的 表结构固定的项目
mybatis比较适用于功能模块多变 表结构复杂的项目
2 mybatis案例1
2.1 创建项目:java项目即可
2.2 导入jar包:
commons-logging-1.1.1.jar
log4j-1.2.17.jar
mybatis-3.2.8.jar
mysql-connector-java-5.1.15-bin.jar
2.3 创建数据库
use db_11;
CREATE TABLE `student` (
`sid` int(11) NOT NULL AUTO_INCREMENT,
`sname` varchar(11) DEFAULT NULL,
`sex` char(1) DEFAULT NULL,
`score` float(4,1) DEFAULT NULL,
`stid` int(11) DEFAULT NULL,
`sage` int(11) DEFAULT NULL,
`sdy` tinyint(1) DEFAULT NULL,
PRIMARY KEY (`sid`),
KEY `fk_11_s_t` (`stid`),
CONSTRAINT `fk_11_s_t` FOREIGN KEY (`stid`) REFERENCES `teacher` (`tid`)
) ENGINE=InnoDB AUTO_INCREMENT=997 DEFAULT CHARSET=utf8
2.4 根据表创建实体类
public class Student implements Serializable{
private Integer sid;
private String sname;
private String sex;
private Float score;
private Integer stid;
private Integer sage;
private Boolean sdy;
....
}
2.5 创建mybatis的核心配置文件
- mybatis_conf.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="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
- dtd/xsd
document type defined :文档类型定义
给xml文件制定规范:制定xml的根标签+子标签+标签的属性
2.6 创建实体类对应的sql映射文件
- StudentMapper.xml
<?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="/">
<!-- 每一种sql语句对应一种标签 -->
<!-- 标签的id属性必须唯一 -->
<!-- mybatis的sql配置文件中:#{xxx}是占位符 -->
<!-- select标签的属性:
resultType:结果集中"每行"对应的java对象的类型
parameterType:参数的类型
-->
<!-- 当parameterType是单值类型时:#{xxx}占位符中的名字 可以随意 -->
<!-- 当parameterType是对象类型时:#{xxx}占位符中的名字必须和对象的属性名一致 -->
<select id="getOne" resultType="com.zhiyou100.demo01.Student" parameterType="int">
select * from student where sid = #{id}
</select>
<select id="getAll" resultType="com.zhiyou100.demo01.Student">
select * from student
</select>
<delete id="deleteOne" parameterType="int">
delete from student where sid=#{sid}
</delete>
<update id="updateOne" parameterType="com.zhiyou100.demo01.Student">
update student set sname=#{sname},sex=#{sex},sage=#{sage},sdy=#{sdy},score=#{score},stid=#{stid} where sid=#{sid}
</update>
<insert id="addOne" parameterType="com.zhiyou100.demo01.Student">
insert into student(sname,sex,sage,score,stid,sdy) values(#{sname},#{sex},#{sage},#{score},#{stid},#{sdy})
</insert>
</mapper>
2.7 把sql元素文件在mybatis的核心配置文件中关联
- mybatis_conf.xml
<!--引入所有的sql映射文件-->
<mappers>
<mapper resource="src/com/zhiyou100/demo01/StudentMapper.xml"/>
</mappers>
2.8 测试
//1 获取sqlsessionfactorybuilder对象
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//2 sqlsessionfactorybuilder读取核心配置文件 获取sqlsessionfactory对象
File file=new File("src/com/zhiyou100/demo01/mybatis_conf.xml");
SqlSessionFactory factory=builder.build(new FileInputStream(file));
//3 获取sqlsession:类似于connection连接
SqlSession session=factory.openSession(true);//boolean参数表示是否支持事务自动提交
//只有dml才有事务:多个dml组成一个整体 同生共死
//4 通过sqlsession的方法调用sql标签 访问数据库
//获取一个
Student s1=session.selectOne("getOne",51);
System.out.println(s1);
List<Student> list=session.selectList("getAll");
for (Student s : list) {
System.out.println("s="+s);
}
//删除一个
int hang=session.delete("deleteOne", 108);
System.out.println("删除"+hang+"个成功!");
//修改一个
hang=session.update("updateOne", new Student(51, "新xxx", "仙", 111f, 2, 12, true));
System.out.println("修改"+hang+"个成功!");
//添加一个
hang=session.insert("addOne", new Student(null, "xxx", "x", 222f, 2, 22, true));
System.out.println("添加"+hang+"个成功!");
s1=session.selectOne("getOne",51);
System.out.println(s1);
//session.commit();//事务提交
//关闭session
session.close();
3 核心配置文件的改进
3.1 显示sql语句
- 导入jar包:commons-logging-1.1.1.jar+log4j-1.2.17.jar
- 创建一个log4j的核心配置文件:名字必须是log4j.properties 位置必须是src
log4j.logger.com.ibatis=DEBUG
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG
log4j.logger.Java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
- 核心配置文件中添加配置信息
<settings>
<!-- 制定log4j作为日志系统:显示sql语句 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
- 测试
log4j:WARN No appenders could be found for logger (org.apache.ibatis.logging.LogFactory).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 214074868.
==> Preparing: select * from student where sid = ?
==> Parameters: 51(Integer)
<== Columns: sid, sname, sex, score, stid, sage, sdy
<== Row: 51, 新xxx, 仙, 111.0, 2, 12, 1
<== Total: 1
Student [sid=51, sname=新xxx, sex=仙, score=111.0, stid=2, sage=12, sdy=true]
==> Preparing: delete from student where sid=?
==> Parameters: 108(Integer)
<== Updates: 0
删除0个成功!
==> Preparing: update student set sname=?,sex=?,sage=?,sdy=?,score=?,stid=? where sid=?
==> Parameters: 新xxx(String), 仙(String), 12(Integer), true(Boolean), 111.0(Float), 2(Integer), 51(Integer)
<== Updates: 1
修改1个成功!
==> Preparing: insert into student(sname,sex,sage,score,stid,sdy) values(?,?,?,?,?,?)
==> Parameters: xxx(String), x(String), 22(Integer), 222.0(Float), 2(Integer), true(Boolean)
<== Updates: 1
添加1个成功!
==> Preparing: select * from student where sid = ?
==> Parameters: 51(Integer)
<== Columns: sid, sname, sex, score, stid, sage, sdy
<== Row: 51, 新xxx, 仙, 111.0, 2, 12, 1
<== Total: 1
Student [sid=51, sname=新xxx, sex=仙, score=111.0, stid=2, sage=12, sdy=true]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@cc285f4]
Returned connection 214074868 to pool.
3.2 给实体类起个别名
方式1:每个实体类起个不同的别名
- 在核心配置文件中添加
<!-- 给实体类起别名 -->
<typeAliases>
<typeAlias type="com.zhiyou100.demo01.Student" alias="stu"/>
</typeAliases>
- 在sql映射文件中 可以有别名来替代类的全称
<select id="getOne" resultType="com.zhiyou100.demo01.Student" parameterType="int">
select * from student where sid = #{id}
</select>
<select id="getAll" resultType="stu">
select * from student
</select>
<delete id="deleteOne" parameterType="int">
delete from student where sid=#{sid}
</delete>
<update id="updateOne" parameterType="stu">
update student set sname=#{sname},sex=#{sex},sage=#{sage},sdy=#{sdy},score=#{score},stid=#{stid} where sid=#{sid}
</update>
<insert id="addOne" parameterType="com.zhiyou100.demo01.Student">
insert into student(sname,sex,sage,score,stid,sdy) values(#{sname},#{sex},#{sage},#{score},#{stid},#{sdy})
</insert>
方式2:指定实体类包名即可
- 在核心配置文件中添加
<!-- 给实体类起别名 -->
<typeAliases>
<package name="com.zhiyou100.demo01"/>
</typeAliases>
- 在sql映射文件中直接用类名即可
<select id="getOne" resultType="Student" parameterType="int">
select * from student where sid = #{id}
</select>
<select id="getAll" resultType="Student">
select * from student
</select>
<delete id="deleteOne" parameterType="int">
delete from student where sid=#{sid}
</delete>
<update id="updateOne" parameterType="Student">
update student set sname=#{sname},sex=#{sex},sage=#{sage},sdy=#{sdy},score=#{score},stid=#{stid} where sid=#{sid}
</update>
<insert id="addOne" parameterType="com.zhiyou100.demo01.Student">
insert into student(sname,sex,sage,score,stid,sdy) values(#{sname},#{sex},#{sage},#{score},#{stid},#{sdy})
</insert>
3.3 对数据库四大参数单独配置
- 创建属性集文件装四大参数:db.properties
jdbc.driverName=com.mysql.jdbc.Driver
jdbc.name=root
jdbc.pwd=root
jdbc.url=jdbc:mysql://localhost:3306/db_11
- 在核心配置文件中引入此属性集文件
<!-- 引入四大参数的属性集文件 -->
<properties resource="com/zhiyou100/demo01/db.properties"/>
- 使用数据时 通过${}来获取
<environments default="e1">
<environment id="e1">
<!-- 制定事务管理方式 -->
<transactionManager type="JDBC"/>
<!-- 制定使用内置连接池:并制定连接数据库的四大参数 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.name}"/>
<property name="password" value="${jdbc.pwd}"/>
</dataSource>
</environment>
</environments>
4 内容补充
4.1 属性集文件
后缀名是.properties
格式:属性名=属性值
作用:作为配置文件:数据之间没有关系 只有单一的名字+值这样类型的数据
- 解析属性集文件
//创建properties对象
Properties pros=new Properties();
//关联属性集文件
File file=new File("src/com/zhiyou100/demo01/db.properties");
pros.load(new FileInputStream(file));
//获取属性值
System.out.println(pros.getProperty("jdbc.driverName"));
System.out.println(pros.getProperty("jdbc.name"));
System.out.println(pros.getProperty("jdbc.url"));
System.out.println(pros.getProperty("jdbc.pwd"));
//遍历:和遍历hashmap相同:
//获取所有的键对应的set 通过键获取值
//Set<Object> keys=pros.keySet();
//获取所有键值对对象对应的set 通过Entry的getKey和getValue获取键和值
//Set<Entry<Object, Object>> entrys=pros.entrySet();
4.2 log4j日志系统
- 日志系统
系统日志是记录系统中硬件、软件和系统问题的信息,同时还可以监视系统中发生的事件,对用户操作进行统计。。。
分类:服务器日志 用户操作日志 错误日志 数据库日志
- log4j
log for java 为java写的一个日志系统
- 步骤1:导入jar
log4j-1.2.17.jar
- 在classes下创建log4j的核心配置文件
位置必须是src
名字必须是log4j.properties
#日志级别
#A:off 最高等级,用于关闭所有日志记录。
#B:fatal 指出每个严重的错误事件将会导致应用程序的退出。
#C:error 指出虽然发生错误事件,但仍然不影响系统的继续运行。
#D:warm 表明会出现潜在的错误情形。
#E:info 一般和在粗粒度级别上,强调应用程序的运行全程。
#F:debug 一般用于细粒度级别上,对调试应用程序非常有帮助。
#G:all 最低等级,用于打开所有日志记录。
# 指定统一的日志级别为INFO
# 指定两个记录官:stdout , R
log4j.rootCategory=INFO, stdout , R
#记录官stdout:类型是ConsoleAppender--要把信息打印到控制台
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
#记录官stdout:日志信息格式是自定义的
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#记录官stdout:日志信息格式
log4j.appender.stdout.layout.ConversionPattern=[QC] %p [%t] %C.%M(%L) | %m%n
#记录官stdout:类型是DailyRollingFileAppender--每天生成一个新的文件
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.File=E:\\log4.log
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n
log4j.logger.com.neusoft=DEBUG
log4j.logger.com.opensymphony.oscache=ERROR
log4j.logger.net.sf.navigator=ERROR
log4j.logger.org.apache.commons=ERROR
log4j.logger.org.apache.struts=WARN
log4j.logger.org.displaytag=ERROR
log4j.logger.org.springframework=DEBUG
log4j.logger.com.ibatis.db=WARN
log4j.logger.org.apache.velocity=FATAL
log4j.logger.com.canoo.webtest=WARN
log4j.logger.org.hibernate.ps.PreparedStatementCache=WARN
log4j.logger.org.hibernate=DEBUG
log4j.logger.org.logicalcobwebs=WARN
- 创建测试类
package test;
import org.apache.log4j.Logger;
public class Test02Log4j {
//获取logger对象
private static Logger logger=Logger.getLogger(Test02Log4j.class);
public static void main(String[] args) {
//添加日志信息
logger.fatal("fatal....开始");
logger.error("error....开始");
logger.warn("warn.....开始");
logger.info("info....开始");
logger.debug("debug....开始");
System.out.println("1+1=?");
logger.fatal("fatal....算术过程中");
logger.error("error....算术过程中");
logger.warn("warn.....算术过程中");
logger.info("info....算术过程中");
logger.debug("debug....算术过程中");
System.out.println("1+1=2!");
logger.fatal("fatal....结束");
logger.error("error....结束");
logger.warn("warn.....结束");
logger.info("info....结束");
logger.debug("debug....结束");
}
}
5 数据库设计三大范式
- 数据库设计时 需要遵守的基本规则
第一范式1NF:保证列的原子性
列数据的不可分割
- 错误案例
create table student(
sid int primary key,
sname varchar(11),
saddress varchar(200)
);
insert into student values(1,"韩梅梅","河南省郑州市二七区京广路街道新月家园1单元2000室");
insert into student values(2,"韩梅1","河南郑州二七区淮河路街道升龙国际2单元3001室");
-- 获取河南学生 对saddress需要进行截取
- 正确案例
create table student(
sid int primary key,
sname varchar(11),
saddress_province varchar(10),
saddress_city varchar(10),
saddress_street varchar(10),
saddress_detail varchar(20)
);
insert into student values(1,"韩梅梅","河南省","郑州市","二七区京广路街道","新月家园1单元2000室");
insert into student values(2,"韩梅1","河南省","郑州市","二七区淮河路街道","升龙国际2单元3001室");
第二范式2NF:所有列必须直接依赖主键列
消除传递依赖,一个表只描述一个事物
- 错误案例
create table student(
sid int primary key,
sname varchar(11),
school_name varchar(11),
school_address varchar(20),
school_type varchar(10),
school_no varchar(10)
);
insert into student values(1,"韩梅梅","陇西路小学","河南省郑州市二七区","公立小学","NO10001");
insert into student values(3,"韩2","陇西路小学","河南省郑州市二七区","公立小学","NO10001");
问题:1:student包含了描述学生的信息 还 包含了描述学校的信息
2:同一个学校的学生记录中 学校的信息会出现多次
- 正确案例
把学校相关的学校定义为学校表 在学生表中引用学校表的主键即可
create table student(
sid int primary key,
sname varchar(11),
schoolid varchar(11),
constraint kf_1 foreign key (schoolid) references school(schoolid)
);
create table school(
schoolid varchar(11),
school_name varchar(11),
school_address varchar(20),
school_type varchar(10)
);
insert into student values(1,"韩梅梅","NO10001");
insert into student values(3,"韩2","NO10001");
insert into school values("NO10001","陇西路小学","河南省郑州市二七区","公立小学");
第三范式3NF:所有列必须完全依赖主键列
消除部分依赖:只针对于 联合主键
- 错误案例:订单详情表
create table order_detail(
order_id int comment "订单编号",
product_name varchar(11) comment "商品名字",
product_number int comment "商品数量",
product_price decimal comment "商品单价",
discount float comment "商品折扣",
money decimal comment "总金额",
constraint kf_2 foreign key (order_id) references order(order_id),
primary key(order_id,product_name)
);
# product_price和discount 只依赖联合主键的product_name部分:
# 再定义一个商品表
- 正确案例:把部分依赖product_name的信息写道商品表中
create table order_detail(
order_id int comment "订单编号",
product_id int comment "商品编号",
product_number int comment "商品数量",
money decimal comment "总金额",
constraint kf_21 foreign key (order_id) references order(order_id),
constraint kf_22 foreign key (product_id) references product(product_id),
primary key(order_id,product_id)
);
create table product(
product_id int primary key comment "商品编号",
product_name varchar(11) comment "商品名字",
product_price decimal comment "商品单价",
discount float comment "商品折扣"
);
6:mybatis的两种方式
方式1:原始dao方式
通过sqlsession的自带的方法(selectOne/selectLsit/delete/update/insert方法),指定标签id 调用sql配置文件中的sql标签
缺点:1 需要指定标签的id(是字符串类型) 存在硬编码 编译器无法判断是否正确
只有运行时 通过判断是否出现运行时异常来判断是否错误
2 调用sqlsession的方法传递参数时:id值和参数类型、参数个数 必须参照sql映射文件
不利于分组开发 效率低
方式2: mapper代理方式
通过sqlsession的getMapper方法获取sql映射文件对应的接口的代理对象 通过代理对象的方法来对应sql文件中对应的标签
优点:1 把运行时异常提前到编译时期:编译器会对方法和参数进行检查
2 符合java完全面向对象的思想
- 创建sql映射文件对应的接口:接口的方法声明必须和sql标签一致:id+resultType+paramtertype
public interface StudentMapper {
Student getOne(int sid);
List<Student> getAll();
int updateOne(Student s);
int deleteOne(int sid);
int addOne(Student s);
}
- 在sql映射文件中通过namespace属性来指定对应的mapper接口
<mapper namespace="com.zhiyou100.demo02.StudentMapper">
...
</mapper>
- 通过sqlsession的getMapper方法获取sql映射文件对应的接口的代理对象 通过代理对象的方法来对应sql文件中对应的标签
//通过sqlsession获取StudentMapper接口的代理对象
StudentMapper studentMapper=session.getMapper(StudentMapper.class);
//通过代理对象的方法调用sql标签
studentMapper.getOne(141);
studentMapper.getAll();
studentMapper.deleteOne(51);
studentMapper.updateOne(new Student(141, "hhhh", "圣", 111f, 2, 12, true));
studentMapper.addOne(new Student(null, "和二号和", "人", 111f, 2, 12, true));
- 静态获取
public class MybatisUtil {
static SqlSessionFactory sqlSessionFactory = null;
static {
try {
//使用Mybatis第一步 :获取sqlSessionFactory对象
String resource = "mybatis_conf.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例.
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
}
7:补充#{},${}的区别
(1)#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by "111",如果传入的值是id,则解析成的sql为order by "id"。
(2)$将传入的数据直接显示生成在sql中。如:order by $user_id$,如果传入的值是111,那么解析成sql时的值为order by user_id, 如果传入的值是id,则解析成的sql为order by id。
(3)#方式在很大程度上能够防止sql注入。
(4)$方式无法防止sql注入。
(5)$方式一般用于传入数据库对象,例如传入表名。
(6)一般能用#的就别用$。