目录
(3)对JDBC进行封装,并实现从.properties配置文件中读取连接信息
JDBC
Java Database Connect,SUN公司官方提供的Java访问数据库的接口。
编程前:
在项目中新建一个lib目录,将mysql-connector-java的jar包复制到lib下,并将lib设置为库,be like:
编程案例:
第一个使用Statement,第二个使用PreparedStatement
Statement跟PreparedStatement的对比:
①Statement执行静态SQL语句,它的执行方式是直接将SQL语句发送给数据库,由数据库执行和解析,存在SQL注入的风险。
②PreparedStatement的执行方式是先将参数化的SQL语句发送给数据库进行预编译,再将参数传递给预编译后的SQL语句执行,可以有效防止SQL注入,提升程序安全性。
(1)TestStatement
由下方代码,Statement类执行的sql命令是由字符串直接拼接而成的。
public class TestStatement {
public static void main(String[] args) {
Connection conn = null;
try {
//加载驱动mysql8.0版本
Class.forName("com.mysql.cj.jdbc.Driver");
//建立连接,连接到myschool库
conn = DriverManager.getConnection("jdbc:mysql:///myschool", "root", "");
//编写SQL语句
String name = "Saynoon";
String password = "hello";
String sql = "insert into `users`(`username`,`password`) values('" + name + "','" + password + "')";
//创建命令对象
Statement statement = conn.createStatement();
//执行命令对象
statement.executeUpdate(sql);
System.out.println("success");
}catch (Exception e){
e.printStackTrace();
}finally {
try {
conn.close();
}catch (SQLException e){
e.printStackTrace();
}
}
}
}
(2)TestPreparedStatement
而PreparedStatement执行的sql命令则是将值以参数传输到预编译命令中
public class TestPreparedStatement {
public static void main(String[] args) {
Connection conn = null;
try{
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///myschool","root","");
String sql = "insert into `users`(`username`,`password`) values (?,?)";
//预编译命令,用?代替未知项
PreparedStatement preparedStatement = conn.prepareStatement(sql);
//给未知项赋值,索引从1开始
preparedStatement.setString(1,"Saynoon2");
preparedStatement.setString(2,"hello");
//执行命令对象
preparedStatement.execute();
System.out.println("success");
} catch (Exception e){
e.printStackTrace();
} finally {
try {
conn.close();
}catch (SQLException e){
e.printStackTrace();
}
}
}
}
(3)对JDBC进行封装,并实现从.properties配置文件中读取连接信息
以下代码中,关于Object类的一些小知识点:
①Object类是每个类的直接或间接父类;Object类的引用可以存储任意类型的对象;Object类的方法是每个类的默认功能方法。
②Object类的类型转换:
以Object类转为int/Integer为例:
一是强制类型转换:(Integer) object,此时兼容对象object为null的情况
二是先用toString方法将Object对象转为String类型,再用Integer的valueof/parseInt方法;此时object不能为null
将object转为Integer对象:Integer.valueof(object.toString())
将object转为int类型:Integer.parseInt(object.toString())
关于ResultSet类的小知识点:
next()方法:第一次执行时,读取ResultSet第一行的结果;next方法执行成功时,就会返回true,失败则返回false。
get***()方法:和next()方法结合使用,获取ResultSet某行数据某个属性的值;该方法参数有两种:get***(int columnIndex)、get***(String columnLabel),即列序号、属性名。
public class JDBCConfigUtil {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
protected Connection conn;
//静态代码块
static {
try{
//获取当前对象所属的class对象的类加载器,使用类加载器的方法加载资源作为输入流
InputStream inputStream = JDBCConfigUtil.class.getClassLoader().getResourceAsStream("database.properties");
//使用Properties类,从.properties文件输入流中,加载属性列表到Properties对象中
Properties properties = new Properties();
properties.load(inputStream);
//通过getProperty方法在属性列表中搜索属性,动态获取配置文件的数据
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
Class.forName(driver);
}catch (Exception e){
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
public boolean executeUpdate(String sql, Object[] params){
try {
conn = getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
try{
PreparedStatement preparedStatement = conn.prepareStatement(sql);
preparedStatement = matchType(preparedStatement, params);
if (preparedStatement.executeUpdate() > 0){
return true;
}
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
public ResultSet executeQuery(String sql, Object[] params){
try {
conn = getConnection();
}catch (SQLException e){
e.printStackTrace();
}
try {
PreparedStatement preparedStatement = conn.prepareStatement(sql);
preparedStatement = matchType(preparedStatement, params);
return preparedStatement.executeQuery();
}catch (SQLException e){
e.printStackTrace();
}
return null;
}
public void close(){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public PreparedStatement matchType(PreparedStatement preparedStatement, Object[] params) {
try {
for (int i = 0; i < params.length; i++) {
Object param = params[i];
if (param instanceof Integer) {
//强制类型转换,兼容param为null的情况
//preparedStatement.setInt(i + 1, (Integer) param);
//不兼容param为null的情况:
//一是转为Integer对象
//preparedStatement.setInt(i + 1, Integer.valueOf(param.toString()));
//二是转为int变量类型
preparedStatement.setInt(i + 1, Integer.parseInt(param.toString()));
} else if (param instanceof Double) {
//preparedStatement.setDouble(i + 1, (Double) param);
preparedStatement.setDouble(i + 1, Double.parseDouble(param.toString()));
} else if (param instanceof Float) {
//preparedStatement.setFloat(i + 1, (Float) param);
preparedStatement.setFloat(i + 1, Float.parseFloat(param.toString()));
} else if (param instanceof String) {
//preparedStatement.setString(i + 1, (String) param);
preparedStatement.setString(i + 1, param.toString());
}
}
}catch (Exception e){
e.printStackTrace();
}
return preparedStatement;
}
}
public class TestConfig {
public static void main(String[] args) {
//创建JDBCConfigUtil对象
JDBCConfigUtil jdbcConfig = new JDBCConfigUtil();
//写SQL更新语句
String updateSql = "insert into `users`(`username`,`password`) values (?,?)";
//封装参数
Object[] updateParams = {"Saynoon", "hello2"};
//调用JDBCConfigUtil中的写方法,执行命令,输出更新结果
System.out.println(jdbcConfig.executeUpdate(updateSql, updateParams));
//写SQL查询语句
String querySql = "select * from `users` where `username` = ?";
//封装参数
Object[] queryParams = {"Saynoon"};
//调用JDBCConfigUtil中的查方法,执行命令,输出查询结果
ResultSet res = jdbcConfig.executeQuery(querySql, queryParams);
try {
while (res.next()){
//用列索引接收结果
// int id = res.getInt(1);
// String username = res.getString(2);
//用列属性名接收结果
int id = res.getInt("id");
String username = res.getString("username");
System.out.println(id + "\t" + username);
}
}catch (SQLException e){
e.printStackTrace();
}
}
}
Mybatis持久层框架
Mybatis是一种支持动态SQL语句的持久层框架,封装了JDBC操作,可以将SQL语句写在.xml文件中,使SQL语句和Java代码实现分离,方便系统维护和开发。
(1)Mybatis持久层框架的好处:
①封装了JDBC操作,简化代码,实现了SQL语句和Java代码的分离;
②自身支持连接池,可以提高销量,无需反复地创建连接、关闭连接;
ps:连接池相关知识点可见什么是连接池?为什么需要连接池呢?连接池的组成原理又是什么呢?_路遥叶子的博客-CSDN博客
③Mybatis会将查询结果转换为Java对象,不需要手动处理(JDBC返回的是ResultSet对象)。
(2)Mybatis代码实现
①此处我们使用Maven在pom文件中引入Mybatis依赖,当然也可以通过手动下载Mybatis复制到项目lib目录下实现Myabtis导入。
Maven的使用:Maven项目管理工具 简单使用_Sayatnoon的博客-CSDN博客
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
②主配置文件mybatis-config.xml
部分标签知识点:
transcationManager标签:事务管理,可选类型有JDBC、MANAGED,选择JDBC是指将事务交给JDBC管理,使用JDBC的提交、回滚,选择MANAGED是自己管理事务
dataSource标签:选择数据源(也就是连接池),可选JNDI、POOLED、UNPOOLED,一般选择POOLED,即使用连接池。
property标签:是连接数据库的一系列信息,也可以写在专门的数据库配置信息文件中,再在这里引用
mappers标签:管理每一个数据表对应的SQL映射配置文件;每一次新增时一定要在这个标签下添加,否则无法实现SQL语句。
<?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="mysql">
<environment id="mysql">
<!-- 事务管理可选类型:JDBC(使用JDBC的提交、回滚方式)、MANAGED(自己管理事务)-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源,可选类型:JNDI/POOLED(使用连接池)/UNPOOLED-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///myschool"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
<!-- 管理SQL映射配置文件 -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
③实体类
public class User {
private long id;
private String username;
private String password;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
④SQL映射配置文件***Mapper.xml及Dao层(数据访问层)的接口***Mapper
注意:这两个文件需要一一对应绑定,.xml文件的namespace需要写dao层接口信息;两个文件的方法名要一致,且输入的参数名要一致(或者用序号代替);
关于两个文件的参数名绑定,可见:Mybatis中的Mapper配置文件_mybatismapper配置文件_Sayatnoon的博客-CSDN博客
Mapper配置文件的参数使用的#、$的区别在于:
使用格式上,#{**}、'${**}';#{**}是将输入作为字符串,会自动加上双引号,使用了JDBC的preparedStatement预编译命令,所以比‘${**}'更安全,可以防止SQL注入
<?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">
<!-- 绑定到dao层的接口 -->
<mapper namespace="org.example.dao.UserMapper">
<!-- 绑定方法,必须同名 -->
<select id="getUserById" resultType="org.example.entity.User" parameterType="Integer">
SELECT * FROM users WHERE id = #{id}
</select>
</mapper>
import org.example.entity.User;
public interface UserMapper {
User getUserById(int id);
}
⑤MybatisUtil类,用于获取/关闭SqlSession(会话)
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MybatisUtil {
static SqlSessionFactory sqlSessionFactory = null;
//获取sqlSession
public static SqlSession getSession(){
SqlSession session = null;
InputStream ins = null;
try{
//加载主配置文件
ins = Resources.getResourceAsStream("mybatis-config.xml");
//获取Mybatis的SqlSession工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(ins);
//通过工厂获取session
session = sqlSessionFactory.openSession(true);//true表示自动提交事务
//调用session的查询集合方法
return session;
}catch (IOException e){
e.printStackTrace();
}finally {
try {
ins.close();
}catch (IOException e){
e.printStackTrace();
}
}
return null;
}
//关闭SqlSession
public static void closeSession(SqlSession session){
if (session != null){
session.close();
}
}
}
⑥使用方法连接数据库并执行SQL语句
btw,这里用了junit,在pom.xml中也要引入依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
import org.apache.ibatis.session.SqlSession;
import org.example.dao.UserMapper;
import org.example.entity.User;
import org.example.util.MybatisUtil;
import org.junit.Test;
public class testMybatis {
@Test
public void test() {
SqlSession session = MybatisUtil.getSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.getUserById(1);
if (user != null) {
System.out.println(user.getUsername());
}
}
}
(3)Mybatis的一些知识点
①SqlSessionFactory
SqlSessionFactory是Mybatis框架的重要接口,用于生产SqlSession,即会话。而SqlSessionFactory对象的实例则是由SqlSessionFactoryBuilder读取主配置文件进行创建的。
SqlSessionFactory是线程安全的,一旦被创建,在应用执行期间都应该存在,不建议重复多次创建,建议使用单例模式。
②SqlSession
SqlSession是Mybatis最核心的组件,可以看作是数据库操作的一次会话。
SqlSession对象的实例是由SqlSessionFactory的openSession方法创建的,并且可以在该方法选择是否自动提交事务。
SqlSession封装了对数据库的操作,可以直接调用SqlSession的对象实例的insert、selectOne、selectList等各类方法,执行mapper中定义的方法。可以通过它的getMapper方法获得Mapper对象(原理是动态代理),然后直接执行Mapper中的方法。
SqlSession不是线程安全的,需要保证每个线程都有自己的SqlSession实例,否则容易出现数据混乱的情况。
③Mybatis缓存
一级缓存:指在同一个SqlSession中,如果执行了相同的SQL语句,则Mybatis会将查询结果缓存在内存中,下次查询可以直接从缓存中获取。
二级缓存:指在不同SqlSession中,如果执行了相同的SQL语句,Mybatis会将查询结果缓存在内存中,下次直接从缓存中获取。也就是所有的SqlSession共享同一个Mapper的二级缓存;如果数据被更新,则二级缓存会清空。
在Mybatis中,缓存可以通过配置文件中的标签进行配置。也支持通过插件自定义缓存实现。但缓存也可能导致数据不一致,需要合理配置。
官方不提供三级缓存,不过可以通过自定义插件实现三级缓存。
Mybatis-Plus框架
顾名思义,Mybatis-Plus是Mybatis的plus版。Mybatis-Plus内置Mapper,无需编写mapper.xml文件就能实现大部分的增删查改操作。
Mybatis-Plus代码实现(非Spring环境):
①在Maven中引入Mybatis-Plus依赖,方便后续测试,也顺了个遍引入Junit
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>TestMybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
②主配置文件mybatis-config.xml
这里与Mybatis使用的不同点在于:Mybatis-Plus大多数时候无需编写Mapper.xml,所以Mappers标签下不使用<mapper resource>,可以用<mapper class>指定Mapper接口,或者<package name>指定存放mapper的包。
<?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="mysql">
<environment id="mysql">
<!-- 事务管理可选类型:JDBC(使用JDBC的提交、回滚方式)、MANAGED(自己管理事务)-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源,可选类型:JNDI/POOLED(使用连接池)/UNPOOLED-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///myschool"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</dataSource>
</environment>
</environments>
<!-- 管理SQL映射配置文件 -->
<mappers>
<!-- <package name="org.example.mapper"/>-->
<mapper class="org.example.mapper.UserMapper"/>
</mappers>
</configuration>
③实体类
注意注意,Mybatis-Plus下实体类名称一定要与数据表名称一致(大小写差异不影响),否则匹配时会找不到表
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.activerecord.Model;
public class Users extends Model<Users> {
@TableId(type = IdType.AUTO)
private Integer id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
④数据访问层Mapper接口
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.example.entity.Users;
public interface UserMapper extends BaseMapper<Users> {
}
⑤MybatisPlusUtil
注意:这里的会话工厂建造类不是SqlSessionFactoryBuild,而是MybatisSqlSessionFactoryBuild
public class MybatisPlusUtil {
static SqlSessionFactory sqlSessionFactory = null;
//获取sqlSession
public static SqlSession getSession(){
SqlSession session = null;
InputStream ins = null;
try{
//加载主配置文件
ins = Resources.getResourceAsStream("mybatis-config.xml");
//获取Mybatis的SqlSession工厂
sqlSessionFactory = new MybatisSqlSessionFactoryBuilder().build(ins);
//通过工厂获取session
session = sqlSessionFactory.openSession(true);//true表示自动提交事务
//调用session的查询集合方法
return session;
}catch (IOException e){
e.printStackTrace();
}finally {
try {
ins.close();
}catch (IOException e){
e.printStackTrace();
}
}
return null;
}
//关闭SqlSession
public static void closeSession(SqlSession session){
if (session != null){
session.close();
}
}
}
⑥Test
package org.example;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.session.SqlSession;
import org.example.mapper.UserMapper;
import org.example.entity.Users;
import org.example.util.MybatisPlusUtil;
import org.junit.Test;
import java.util.List;
public class testMybatisPlus {
@Test
public void test(){
SqlSession session = MybatisPlusUtil.getSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
Users user = new Users();
user.setUsername("Mybatis-Plus");
user.setPassword("Hello");
if (userMapper.insert(user) > 0){
System.out.println("success");
}
// QueryWrapper queryWrapper = new QueryWrapper<>();
// queryWrapper.eq("username", "Mybatis-Plus");
// List<Users> users = userMapper.selectList(queryWrapper);
// for (Users user1: users){
// System.out.println(user1.getId() + "\t" + user1.getUsername());
// }
}
}
在许多场景下,比如SpringBoot项目中,Mybatis-Plus框架的使用贯穿实体类、数据访问层、服务层,在每一层都提供了相应的接口给项目中的类继承;在用户端大多时候是调用服务层的方法实现功能。