JDBC
JDBC
JDBC:(Java DataBase Connectivity),就是使用Java语言操作关系型数据库的一套API。
入门程序
准备数据库web01:
create table user(
id int unsigned primary key auto_increment comment 'ID,主键',
username varchar(20) comment '用户名',
password varchar(32) comment '密码',
name varchar(10) comment '姓名',
age tinyint unsigned comment '年龄'
) comment '用户表';
insert into user(id, username, password, name, age) values (1, 'daqiao', '123456', '大乔', 22),
(2, 'xiaoqiao', '123456', '小乔', 18),
(3, 'diaochan', '123456', '貂蝉', 24),
(4, 'lvbu', '123456', '吕布', 28),
(5, 'zhaoyun', '12345678', '赵云', 27);
在 pom.xml 文件中引入依赖:
<dependencies>
<!-- MySQL JDBC driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
</dependencies>
在 src/main/test/java 目录下编写测试类,定义测试方法:
public class JdbcTest {
/*
* JDBC入门程序
*/
@Test
public void testUpdate() throws Exception {
//1.注册驱动
//两次shift提示
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取数据库连接
String url="jdbc:mysql://localhost:3306/web01";
String username="root";
String password="123456";
Connection connection=DriverManager.getConnection(url,username,password);
//3.获取SQL语句执行对象
Statement statement=connection.createStatement();
//4.执行SQL
int i=statement.executeUpdate("update user set age=25 where id=1");//DML
System.out.println("SQL语句执行完毕影响的记录数为:"+i);
//5.释放资源
statement.close();
connection.close();
}
}
查询数据
ResultSet(结果集对象):封装了DQL查询语句查询的结果。
- next():将光标从当前位置(开始默认表头)向前移动一行,并判断当前行是否为有效行,返回值为boolean。
- true:有效行,当前行有数据
- false:无效行,当前行没有数据
- getXxx(…):获取数据,可以根据列的编号获取,也可以根据列名获取(推荐)。
@Test
public void testSelect() throws Exception {
String url="jdbc:mysql://localhost:3306/web01";
String username="root";
String password="123456";
Connection conn=null;
PreparedStatement stmt=null;
ResultSet rs=null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn =DriverManager.getConnection(url,username,password);
String sql="select id,username,password,name,age from user where username = ? and password = ?";
//?是占位符
stmt= conn.prepareStatement(sql);
stmt.setString(1,"daqiao");
stmt.setString(2,"123456");
//封装查询返回的结果
rs=stmt.executeQuery();
while(rs.next()) {
User user = new User(
rs.getInt("id"),
rs.getString("username"),
rs.getString("password"),
rs.getString("name"),
rs.getInt("age")
);
System.out.println(user);//使用lombok的@Data自动生成的toString方法
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(rs!=null) rs.close();
if(stmt!=null) stmt.close();
if(conn!=null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
预编译SQL
SQL注入:通过控制输入来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。
在cmd窗口运行jar包:
java -jar sql_Injection_demo-0.0.1-SNAPSHOT.jar
SELECT * FROM user WHERE username = 'shfhsjfhja' AND password = '' or '1' = '1 '
而通过预编译SQL(select * from user where username = ? and password = ?),就可以直接解决上述SQL注入的问题且性能更高。
MyBatis
- MyBatis是一款优秀的 持久层 框架,用于简化JDBC的开发。
- MyBatis本是 Apache的一个开源项目iBatis,2010年这个项目由apache迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
- 官网:https://mybatis.net.cn/
入门程序
- 创建工程
- 数据准备
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
private String name;
private Integer age;
}
数据库创建同JDBC。
3. 配置属性文件
在 application.properties 中配置数据库的连接信息。
spring.application.name=springboot-mybatis-quickstart
# 配置数据库的连接信息(字符编码在settings中的file encoding中配置)
#数据库访问的url地址
spring.datasource.url=jdbc:mysql://localhost:3306/web01
#数据库驱动类类名
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#访问数据库-用户名
spring.datasource.username=root
#访问数据库-密码
spring.datasource.password=123456
- 创建接口
@Mapper//应用程序在运行时,会自动的为该接口创建一个实现类对象(代理对象),并且会自动将该实现类存入IOC容器-bean
public interface UserMapper {
/*查询所有用户*/
@Select("select * from user")//自动将查询的结果封装给返回值
public List<User> findAll();
}
- 单元测试
在创建出来的SpringBoot工程中,在src下的test目录下,已经自动帮我们创建好了测试类(名字要与启动类相同) ,并且在测试类上已经添加了注解 @SpringBootTest,代表该测试类已经与SpringBoot整合。
该测试类在运行时,会自动通过引导类加载Spring的环境(IOC容器)。我们要测试那个bean对象,就可以直接通过@Autowired注解直接将其注入进行,然后就可以测试了。
@SpringBootTest//SpringBoot单元测试的注解-当前测试类中的方法运行时,会启动springboot项目-IOC容器
class SpringbootMybatisQuickstartApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
public void testFindAll() {
List<User> users = userMapper.findAll();
users.forEach(System.out::println);
}
}
辅助配置
默认我们在UserMapper接口上加的 @Select 注解中编写SQL语句是没有提示的。 如果想让idea给我们提示对应的SQL语句,我们需要在IDEA中配置与MySQL数据库的链接。
配置完成之后,发现SQL语句中的关键字有提示了,但Idea和数据库没有建立连接,不识别表信息。需要在Idea界面右侧配置数据库连接。
除此之外,在File---->Setting--->Languages & Frameworks--->SQL Dialects中,选择对应的数据库,如MySQL,之后点击保存即可。
默认情况下,在Mybatis中,SQL语句执行时,我们并看不到SQL语句的执行日志。 在application.properties加入如下配置,即可查看日志:
#mybatis的配置
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
JDBC VS Mybatis
JDBC程序的缺点:
- url、username、password 等相关参数全部硬编码在java代码中。
- 查询结果的解析、封装比较繁琐。
- 每一次操作数据库之前,先获取连接,操作完毕之后,关闭连接。 频繁的获取连接、释放连接造成资源浪费。
分析了JDBC的缺点之后,我们再来看一下在mybatis中,是如何解决这些问题的:
- 数据库连接四要素(驱动、链接、用户名、密码),都配置在springboot默认的配置文件 application.properties中
- 查询结果的解析及封装,由mybatis自动完成映射封装,我们无需关注
- 在mybatis中使用了数据库连接池技术(类似线程池),从而避免了频繁的创建连接、销毁连接而带来的资源浪费。
数据库连接池
- 数据库连接池是个容器,负责分配、管理数据库连接(Connection)。
- 程序在启动时,会在数据库连接池(容器)中,创建一定数量的Connection对象。
- 释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏。- 客户端获取到Connection对象了,但是Connection对象并没有去访问数据库(处于空闲),数据库连接池发现Connection对象的空闲时间 > 连接池中预设的最大空闲时间,此时数据库连接池就会自动释放掉这个连接对象。
数据库连接池的好处:
- 资源重用
- 提升系统响应速度
- 避免数据库连接遗漏
官方(sun)提供了数据库连接池标准(javax.sql.DataSource接口)。
public Connection getConnection() throws SQLException;
常见的数据库连接池:C3P0 、DBCP 、Druid 、Hikari (springboot默认)
Hikari(追光者)
Druid(德鲁伊)
- Druid连接池是阿里巴巴开源的数据库连接池项目
- 功能强大,性能优秀,是Java语言最好的数据库连接池之一
参考官方地址:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
切换连接池:
在pom.xml文件中引入依赖:
<dependency>
<!-- Druid连接池依赖 -->
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.19</version>
</dependency>
在application.properties中引入数据库连接配置:
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql://localhost:3306/web
spring.datasource.druid.username=root
spring.datasource.druid.password=1234
增删改操作
删除
/**
* 根据id删除
*/
@Delete("delete from user where id = #{id}")
public void deleteById(Integer id);
DML语句执行完毕,是有返回值的,我们可以为Mapper接口方法定义返回值来接收,如下:
/**
* 根据id删除
*/
@Delete("delete from user where id = #{id}")
public Integer deleteById(Integer id);
Integer类型的返回值,表示DML语句执行完毕影响的记录数。
Mybatis的提供的符号,有两个,一个是 #{...},另一个是 ${...},区别如下:
符号 | 说明 | 场景 |
---|---|---|
#{…} | 占位符。执行时,会将#{…}替换为?,生成预编译SQL | 参数值传递 |
${…} | 拼接符。直接将参数拼接在SQL语句中,存在SQL注入问题 | 表名、字段名动态设置时使用 |
新增
@Insert("insert into user(username,password,name,age) values (#{username},#{password},#{name},#{age})")
public void insert(User user);
在测试类中添加测试方法,代码如下:
@Test
public void testInsert() {
User user = new User(null,"gaoyuanyuan","666888","高圆圆",18);
userMapper.insert(user);
}
更新
@Update("update user set username=#{username},password=#{password},name=#{name},age=#{age} where id=#{id}")
public void update(User user);
@Test
public void testUpdate() {
User user = new User(1,"zhouyu","666888","周瑜",20);
userMapper.update(user);
}
查询
定义的接口方法,在java文件编译为字节码文件后,形参名称不会保留,sql语句的占位符无法识别。
/**
* 根据用户名和密码查询用户信息
*/
@Select("select * from user where username = #{username} and password = #{password}")
public User findByUsernameAndPassword(@Param("username") String username, @Param("password") String password);
@param注解的作用是为接口的方法形参起名字的。
- 说明:基于官方骨架创建的springboot项目中,接口编译时会保留方法形参名,@Param注解可以省略 (#{形参名})。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
/**
* 根据用户名和密码查询用户信息
*/
@Select("select * from user where username = #{username} and password = #{password}")
public User findByUsernameAndPassword(String username, String password);
究其原因,sprintboot官方骨架的父工程有个maven项目编译插件,有一个parameters为true的配置项。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<parameters>true</parameters>
</configuration>
</plugin>
XML映射配置
使用Mybatis的注解方式,主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能,建议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中。语法见官网。
- XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)(在resource文件夹下创建,路径用/分隔)
- XML映射文件的namespace属性为Mapper接口全限定名一致
- XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回单条记录类型一致。
<?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.ys.mapper.UserMapper">
<!-- resultType 属性的值,与查询返回的单条记录封装的类型一致-->
<select id="findAll" resultType="com.ys.pojo.User">
select * from user
</select>
</mapper>
public List<User> findAll();
一个接口方法对应的SQL语句,要么使用注解配置,要么使用XML配置,切不可同时配置。
辅助配置
java和resource编译后都会放在target的class目录下,更改xml位置为resource/mapper/UserMapper.xml后,在application.properties属性文件可以修改如下:
#指定xml映射配置文件的位置
mybatis.mapper-locations=classpath:mapper/*.xml
MybatisX的使用
MybatisX是一款基于IDEA的快速开发Mybatis的插件,为效率而生,可以通过MybatisX快速定位。
SpringBoot配置文件
前面我们一直使用springboot项目创建完毕后自带的application.properties进行属性的配置,而如果在项目中,我们需要配置大量的属性,采用properties配置文件这种 key=value 的配置形式,就会显得配置文件的层级结构不清晰,也比较臃肿。
- SpringBoot项目提供了多种属性配置方式(properties、yaml、yml)。
yml配置文件
yml格式配置文件名字为:application.yaml , application.yml 这两个配置文件的后缀名虽然不一样,但是里面配置的内容形式都是一模一样的。基本语法如下:
- 大小写敏感
- 数值前边必须有空格,作为分隔符
- 使用缩进表示层级关系,缩进时,不允许使用Tab键,只能用空格(idea中会自动将Tab转换为空格)
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
- #表示注释,从这个字符一直到行尾,都会被解析器忽略
yml文件中常见的数据格式:
- 对象/Map集合
user:
name: zhangsan
age: 18
password: 123456
- 数组/List/Set集合
hobby:
- java
- game
- sport
在yml格式的配置文件中,如果配置项的值是以 0 开头的,值需要使用 '' 引起来,因为以0开头在yml中表示8进制的数据。("0123")
案例
我们修改下之前案例中使用的配置文件,变更为application.yml配置方式:
spring:
application:
name: springboot-mybatis-quickstart
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/web01
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:mapper/*.xml