- 1、JDBC
- 1-1、简介
- 2-1、入门案例
- 思考总结
- 2、Mybatis(封装简化JDBC)
- 2-1、入门案例(查询)
- 2-2、入门案例二(增加、删除、修改)
- 2-3、常见错误
- 2-4、log4j日志框架
- 2-5、占位符
- 2-5-1、通过Map对象封装
- 2-5-2、通过对象封装
- 2-5-3、#{}, ${}两种占位符的区别
用于java程序连接并访问数据库的一款工具,也是说明书,规定统一了格式,
进行真正的增删改查操作需要由数据库驱动包(jar)包来实现(jar包由不同厂商提供,其中包含了编译后的Class文件,其中又有很多工具类,以实现GRUD)。同时也产生了不同jar包,对应也有不同的API,造成学习成本高的问题,后由sun公司提供了一套接口,要求所有厂商都需实现此接口,实现了API的统一,提供了规范,这个规范就是jdbc
jdbc的jar包已经被java包含,但其中大部分都是接口规范,需要实现具体功能需要导入数据库对应的驱动包
实现流程图:
1、准备好要查询的数据后导包,创建lib目录(Folder),导入mysql驱动包(下载),创建引用(选择右键–> Build Path --> Add To Build Path)
2、导包完成后写JDBC代码
public static void main(String[] args) throws Exception {
//1、注册数据库驱动 将创建mysql驱动包反射对象,会动态将其放入DriverManager进行管理
Class.forName("com.mysql.cj.jdbc.Driver");
//2、获取数据库连接 通过DriverManager类提供方法获取连接,设置端口、数据库和字符集,时区
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jt_db?characterEncoding=utf-8&serverTimezone=Asia/Shanghai"
,"root","root");
System.out.println("连接成功!");
//3、获取传输器,//Statment为接口,规定了格式,如统一方法的名称
Statement stat = connection.createStatement();
//4、发送sql到数据库执行,并返回执行结果 mysql Driver实现的具体方法,
ResultSet rs = stat.executeQuery("select * from account");
//5.处理结果(将查询的结果一行行输出到控制台)
while(rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
double money = rs.getDouble("money");
System.out.println(id+ "," + name+ "," + money);
}
//6.释放资源
rs.close();
stat.close();
connection.close();
}
思考总结补充:事务 (多个sql语句绑定一起执行,考面试)
1、事务四大特性:
原子性:事务作为最小单元不可分割,一条sql语句报错整个sql便都不能执行
一致性:事务执行前后的业务数据之和保持不变
隔离性:事务并发时,事务之间互相隔离
持久性:事务提交后,数据库持久保存在磁盘中
2、事务操作:
默认每一个sql就是一个单独的事务,自动开启提交
事务包含多条sql需手动开启结束
begin开启事务后,需要commit提交事务才会真正修改数据,换成rollback则回滚
| 通过入门案例不难发现JDBC有以下缺点:. |
| 1、大量重复代码,注册连接传输器释放资源.. 2、查询语句处理结果集麻烦,需要一个个列获取(参考入门案例) 3、将SQL语句、连接参数写死在程序中,后期上线维护困难 . |
| 除此之外,JDBC虽然作为传统底层方法,访问速度比第三方框架速度快,但却没有连接池(类似常量池,创建一次后直接从池中拿,无需自身创建连接),导致每次都需自身创建关闭连接,耗时效率低下 . |
| 由此针对JDBC源生代码的缺点,引出了一个优秀的持久层(程序连接数据库的部分)框架——Mybatis |
2、Mybatis(封装简化JDBC) 2-1、入门案例(查询)
- 实现查看数据库表。IDE:Eclipse:Simple maven Project ,maven工程目录如下:
连接关系:EmpMapper.xml<—MybatisDemo01.java—>mybatis-config.xml—>EmpMapper.xml—>pojo.Emp.java
1、MybatisDemo01
- 两处配置文件名、Emp类名可修改,
public class MybatisDemo01 {
public static void main(String[] args) throws Exception {
//1.读取mybatis核心配置文件中的配置信息(mybatis-config.xml)
InputStream in = Resources.getResourceAsStream( "mybatis-config.xml" );
//2.基于上面读取的配置信息获取SqlSessionFactory对象(工厂)
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build( in );
//3.打开与数据库的连接(即通过工厂对象获取SqlSession对象)
SqlSession session = factory.openSession();
//4.通过namespace+id找到并执行SQL语句, 返回处理后的结果
//EmpMapper.xml, List
List list = session.selectList( "EmpMapper.findAll" );
//5.输出结果
for (Emp emp : list) {
System.out.println( emp );
}
}
}
2、POJO.Emp
public class Emp {
//提供私有属性
private Integer id;
private String name;
private String job;
private Double salary;
//提供get、set、toString方法..
3-1、EmpMapper.xml
- namespace:为mapper取名,id:为sql操作取名。
调用:List list = session.selectList( “EmpMapper.findAll” );
3-2、mybatis-config.xml
- default:选中指定id的环境。id:为每个环境做唯一标识。
type:配置事务管理和连接池。value:数据库基本信息。resource:导入类目录下的指定文件
4、pom.xml
- 四个驱动包,加上驱动包的依赖为八个jar包,没有检查maven本地仓库
junit junit 4.9 mysql mysql-connector-java 8.0.11 org.mybatis mybatis 3.2.8 org.slf4j slf4j-log4j12 1.6.4
2-2、入门案例二(增加、删除、修改)
- 修改mybastis01.java代码,提取共用SqlSession对象,使用单元测试@Test,选中方法名运行才可指定执行
- 用@Before保证SqlSession对象已经获取。
- mybatis修改的事务不会自动提交,需要openSession( true )设置自动提交或每次执行完commit手动提交
- 修改Mapper文件增填执行的sql语句
public class MybatisDemo01 {
//提取共需对象做成员变量,供给不同方法使用
SqlSession session = null;
@Before
public void beginMethod() throws Exception {
//1.读取mybatis核心配置文件中的配置信息(mybatis-config.xml)
InputStream in = Resources.getResourceAsStream( "mybatis-config.xml" );
//2.基于上面读取的配置信息获取SqlSessionFactory对象(工厂)
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build( in );
//3.打开与数据库的连接(即通过工厂对象获取SqlSession对象)
session = factory.openSession( true );//开启自动提交事务
//4.通过namespace+id找到并执行SQL语句, 返回处理后的结果
}
//插入 员工信息
@Test
public void insert() {
int rows = session.insert("EmpMapper.insert");
System.out.println("影响行数:"+ rows);
session.commit();
}
//修改 员工信息
@Test
public void update() {
int rows = session.update("EmpMapper.update");
System.out.println("影响行数:"+ rows);
session.commit();
}
//删除 员工信息
@Test
public void delete() {
int rows = session.delete("EmpMapper.delete");
System.out.println("影响行数:"+ rows);
session.commit();
}
}
- EmpMapper.xml
2-3、常见错误
- 1、错误信息: Cause:java.lang.NoSuchMethodException: cn.tedu.pojo.Emp.()
- 原因:pojo实体类初始化失败,说明缺少创建对象的构造方法。Mybatis底层会基于反射创建Emp类的对象实例,
创建使用的无参构造。 - 解决:查看是否时添加了含参构造,导致默认无参构造消失
- 2、错误信息: Mapped Statements collection does not contain value for EmpMapper.finaAll
- 原因:存放sql语句和其对应标识的map集合(Statements collection)中不存在对应的key值。
Statements collection存储形式:EmpMapper.findAll(key) : select * from emp(value) - 解决:Mapper.xml配置文件中的 namespace或id写错了
- 3、错误信息: Mapped Statements collection already contains value for EmpMapper.update
- 原因:Statements collection中已经存在相同的key值,Statements collection以map形式存储: namespace+id(key):sql(value)
- 解决:检查mapper文件中标签的id值是否重复。
- 4、查询结果封装不到实体类对象中。
- 原因:封住查询结果实体类中,会先寻找实体类是否提供提供get、set方法(注意方法名要与属性对应),
没有提供则通过通过暴力反射将数据库表中列名赋值给同名的属性。 - 解决:一般将实体类类型设为包装类,防止出现基本类型默认值。同时提供get、set方法用于封装结果集,
保持属性名和数据表列名一致。
- 专门为Java语言提供的一个日志框架, 可以通过log4j打印程序中的日志信息,
- mybatis默认支持log4j框架。 使用只需 1)导入log4j的jar包
2)导入log4j的配置文件(log4j.properties)即可, 配置文件内容如下:
# Global logging configuration
log4j.rootLogger=DEBUG, Console, LOGFILE
# Console output...
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%5p [%t] %d{yyyy-MM-dd hh:mm:ss} - %m%n
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.file=./mylog.log
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%5p [%t] %d{yyyy-MM-dd hh:mm:ss} - %m%n
1、DEBUG:日志级别,低于设置级别的信息不输出。(比如设置成error则不输出DEBUG,error级别改于DEBUG)。
Console, LOGFILE:设置输出到控制台和外部文件
2、ConsoleAppender:日志输出器,往指定的地方输出信息,ConsoleAppender指定往控制台,FileAppender指定往文件输出
3、PatternLayout、.ConversionPattern:两者配合使用,Pattem设置输出格式,%5p:日志界别(限制五个字符),%t:线程名称,%m:消息内容,%n换行…更多参数配置参考:log4j日志pattern配置。【】、空格 、-、%5 只是为了输出美观,设置还是以 %参数 的形式设置。
4、输出到文件写法与输出到控制台类似,多设置一行输出目录file=./mylog.log即可
- 数据由前端动态传递,通过占位符在sql中的参数值进行占位,等待数据传递赋值
- mybatis中有两种占位符, 分别是: #{}, ${},
其中最常用的就是 #{},占位符两者使用需要注意有所区别 - 传入的数据往往包含多个(id,name,job…),而session提供的增删改查的方法只能传入两个参数(一个已经被 namespace+id 所占据),所以需要将数据进行封装(Map或实体类对象),后通过占位符将其中的数据进行拆分填充,形成sql语句,完成查询功能。
- Map集合的Key要和占位符名称保持一致
@Test
public void insert2() {
Map map = new HashMap();
map.put("name", "马云");
map.put("job", "教师");
map.put("salary",1800);
int rows = session.insert("EmpMapper.insert2",map);
System.out.println("影响行数:"+ rows);
session.commit();
}
mybatis占位符:#{} 翻译成 JDBC的占位符:(?),如果占位符名称与map的key不一致,便会如下图导入插入一个null值。
原因是:占位符名称接收到value值找不到对应Key值进行存放。当只有一个占位符时才不需要名称保持一致。
- 对象的get方法要与占位符名称对应( 如:getIName,#{name} )或
对象实体类中有与占位符同名的属性名(如:private String job; #{job})
@Test
public void update2() {
//创建实体类对象将数据封装到对象中进行传递
Emp emp = new Emp();
emp.setName("马云");
emp.setJob("CEO");
emp.setSalary(80000.0);
int rows = session.update("EmpMapper.update2",emp);
System.out.println("影响行数:"+ rows);
session.commit();
}
将属性名job修改为new_job会出现以下错误:
There is no getter for property named ‘new_job’ in ‘class com.java.pojo.Emp’
则说明new_job没有get方法,同时pojo实体类中没有这个属性名。
结论:占位符要与对象的get()方法名称保持对应,或对象类中存在同名私有属性
使用#{}动态查询指定列时出现的问题:
@Test
public void findAll2() {
List list = session.selectList("EmpMapper.findAll2","id,name,job");
for (Emp emp : list) {
System.out.print(emp);
}
}
查询结果为null:
| 原因:使用 #{ } 会自动拼接引号,导致sql语句变为: select ‘id,name,job’ from emp; 变成了查询‘id,name,job’为一个整体、不存在的列 |
| 解决: 使用 ${ } 让其不拼接引号,使其变成一个我们所想要的sql,同时要求使用${ }中无论有多少个数据,必须传入一个封装好的类型。具体解决代码如下: |
@Test
public void findAll2() {
HashMap map = new HashMap();
map.put("colname", "id,name,job");
List list = session.selectList("EmpMapper.findAll2",map);
for (Emp emp : list) {
System.out.println(emp);
}
}
}



