写在前面
本文看下Java操作数据库相关的内容。
1:JDBC
我们知道关系型数据库不止一种,像MySQL,Oracle,db2,PostgreSQL,sql server等,为了规范对这些不同数据的连接,数据的CRUD操作, Java官方便设计了一套接口,如DriverManager,Connection,Statement,ResultSet,DriverManager等,用来规范这些操作,这个规范就是JDBC在,rt.jar包的中的java.sql包下,如下图:
JDBC和应用程序,以及各种数据库之间的关系如下图:
那么最终如何连接到数据库并操作数据库呢,这就需要不同的数据库厂商按照JDBC的规范来提供一个具体的实现,这个具体的实现我们叫做是数据库驱动
,如MySQL的驱动包mysql-connector-java-xxx.jar
,oracle的驱动包ojdbc-xxx.jar
,接下来我们也分别以MySQL和Oracle为例子看下如何操作数据库。
1.1:MySQL
源码 。
首先引入pom:
<dependencies>
<dependency>
<!--
<groupId>mysql</groupId>
<artifactId>mysql</artifactId>
<version>5</version>
<systemPath>${project.basedir}/jar/mysql-connector-java-5.1.47.jar</systemPath>
-->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
接着写测试程序大家自己创建表a0707,然后创建两个字段录数据就行
:
public class MySqlMain {
public static void main(String[] args) throws Exception {
//1、导入驱动jar包
//2、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//3、获取数据库的连接对象
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3366/test", "root", "root");
//4、定义sql语句
String sql = "SELECT * FROM a0707 ";
//5、获取执行sql语句的对象
Statement stat = con.createStatement();
//6、执行获取结果
ResultSet rs = stat.executeQuery(sql);
while(rs.next()){
String one = rs.getString(1);
String two = rs.getString(2);
System.out.println("one is: " + one + ", two is: " + two);
}
}
}
运行:
one is: 1, two is: xxx
one is: 2, two is: d7df5f01-fdc7-11ec-b06f-00ff2dadabf5
one is: 3, two is: d7df7a50-fdc7-11ec-b06f-00ff2dadabf5
one is: 4, two is: d7df7ad2-fdc7-11ec-b06f-00ff2dadabf5
...
这样我们就通过MySQL的JDBC驱动程序成功操作MySQL数据库了。
1.2:Oracle
在这里 下载驱动包。
首先配置驱动包:
<dependency>
<groupId>oracle</groupId>
<artifactId>oracle</artifactId>
<version>5</version>
<scope>system</scope>
<systemPath>${project.basedir}/jar/ojdbc-10g.jar</systemPath>
</dependency>
这里的systemPath根据本地环境自行修改,当然如果有线上资源的话,直接用也行。
接着写测试程序大家自己创建表AREA,然后创建两个字段录数据就行
:
public class OracleMain {
public static void main(String[] args) throws Exception {
//1、导入驱动jar包
//2、注册驱动
Class.forName("oracle.jdbc.driver.OracleDriver");
//3、获取数据库的连接对象
Connection con = DriverManager.getConnection("jdbc:oracle:thin:@192.168.10.251:1521/orcl", "jc6_jcs", "jinher.2023");
//4、定义sql语句
String sql = "SELECT * FROM AREA ";
//5、获取执行sql语句的对象
Statement stat = con.createStatement();
//6、执行获取结果
ResultSet rs = stat.executeQuery(sql);
while(rs.next()){
String one = rs.getString(1);
String two = rs.getString(2);
System.out.println("one is: " + one + ", two is: " + two);
}
}
}
运行:
one is: 337, two is: 繁峙县
one is: 338, two is: 宁武县
one is: 339, two is: 静乐县
...
这样我们就通过oracle的JDBC驱动程序成功操作oracle数据库了。
可以看到,虽然操作了完全不同的数据库,但是我们只需要引入对应的驱动包,然后修改驱动类,以及数据库地址信息就行了,核心的数据操作逻辑完全不用动,这就是抽象的好处,规范的好处。
2:数据库连接池
我们知道线程资源比较珍贵,而且创建的成本很高,所以我们就有了连接池技术 ,数据库连接也同样如此,所以,我们也同样需要数据库连接的一种池化的技术,也就是数据库连接池
,数据库连接池构建于JDBC之上,通过JDBC获取一组Connection,并按照一定的策略对其进行维护,如最大连接数,最小连接数,空闲多久回收等,如下图:
主要的数据库连接池实现如下:
C3P0:目前用到不多
DBCP:apatch common pool
Druid:阿里大牛开发,提供了sql审计功能,目前有一定市场
hikari:海卡里,日语“光”的意思,目前性能最好的数据库连接池,用的比较多
3:orm
源码 。
orm全称是object relation mapping,即对象关系映射,描述的是Java中的对象和数据库表的一种映射关系。数据库的表和Java的对象是差一层的,通过Java的对象不能直接映射到表,因此这就需要做一个转换,而这个转换的工作也势必是需要有一种规范的,因此SUN公司就是提出了JPA,Java persistence API,是由一组接口和抽象类组成的Java类到数据库表映射的一个规范,如下图:
实现了JPA规范的orm框架事实上的标准就是hibernate,其他的框架还有toplink,openJpa等,应用程序使用JPA规范,底层是具体实现了JPA规范的orm框架,如下图:
实际上,各种orm框架底层和数据库的交互还是通过JDBC完成的,因此可以认为JPA是在JDBC基础上以面向对象操作数据库的方式进行了进一步的封装,如下图:
除了hibernate,我们常用的orm框架还有mybatis,但是不同于hibernate,mybatis并没有遵循JPA规范,而是直接基于JDBC开发的,允许开发人员以直接写sql语句,并通过resultmap的方式映射到对象,接下来我们分别看下hibernate和mybatis的用法。
3.1:hibernate
首先我们创建表:
CREATE TABLE `user` (
`id` int(11) NOT NULL,
`user_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_account` (`user_name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
接着引入依赖:
<dependencies>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<!-- Mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<!-- jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/taglibs/standard -->
<!-- taglibs -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>${taglibs.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-jsp-api -->
<!-- tomcat -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jsp-api</artifactId>
<version>${tomcat.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
<!-- hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
创建映射表的Java对象:
@Getter
@Setter
@AllArgsConstructor
public class User implements Serializable{
private Integer id;
private String userName;
}
创建映射java对象和表的配置文件User.hbm.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- 实体类映射文件 -->
<hibernate-mapping>
<!--
name:实体类全路径名
table:实体类对于的数据库表名称
-->
<class name="dongshi.daddy.User" table="user">
<!--
id:用于设置数据库表结构中主键列的生成方式
name:实体类中属性名称
type:Jave的数据类型
column:数据库表字段名称
-->
<id name="id" type="java.lang.Integer" column="id">
<!--
class:定义主键列生成的方式:hibernate管理、数据库管理、开发者管理
increment,identity,sequcene,native,assgine
-->
<generator class="increment"></generator>
</id>
<!-- 与实体类相匹配 -->
<property name="userName" type="java.lang.String" column="user_name"/>
</class>
</hibernate-mapping>
配置hibernate的配置文件hibernate.cfg.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 数据库相关配置 -->
<!-- connection.username|connection.password|connection.url|connection.driver_class|dialect -->
<!-- 连接数据库账户名称 -->
<property name="connection.username">root</property>
<!-- 连接数据库密码(我的数据库没有登录密码,直接不用写) -->
<property name="connection.password">root</property>
<!-- 连接的绝对路径(使用&需要解译&) -->
<property name="connection.url">
jdbc:mysql://localhost:3366/test?useUnicode=true&characterEncoding=UTF-8&userSSL=false
</property>
<!-- 驱动的绝对路径 -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- 数据库方言配置 -->
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
<!-- 配置本地事务 -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- 调试相关配置 -->
<!-- hibernate运行过程是否展示sql命令代码(自动生成) -->
<property name="show_sql">true</property>
<!-- 是否规范输出sql代码 -->
<property name="format_sql">true</property>
<!-- 实体映射相关配置 -->
<mapping resource="hbm/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
测试类:
public class HibernateJpaMain {
public static void main(String[] args) {
//创建Hibernate核心类
Configuration cfg=new Configuration();
//读取核心配置文件
cfg.configure("hibernate.cfg.xml");
//创建session工厂
SessionFactory sf = cfg.buildSessionFactory();
//获取session
Session session = sf.openSession();
//开启事务
Transaction ts= session.beginTransaction();
System.out.println("-------------增加-------------------");
//新增
User user=new User(111, "阿三同学");
session.save(user);
ts.commit();
}
}
运行:
INFO: HHH000182: No default (no-argument) constructor for class: dongshi.daddy.User (class must be instantiated by Interceptor)
-------------增加-------------------
Hibernate:
select
max(id)
from
user
Hibernate:
insert
into
user
(user_name, id)
values
(?, ?)
查看表:
3.2:mybatis
首先我们创建表:
CREATE TABLE test_mybatis (
id INT(32) PRIMARY KEY AUTO_INCREMENT,
full_name VARCHAR(64) DEFAULT NULL,
age INT(32)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
创建实体类:
@Getter
@Setter
@AllArgsConstructor
public class TestMybatis {
private Integer id;
private String fullName;
private Integer age;
}
创建mapper接口:
public interface TestMyBatisMapper {
void insertTestMybatis(TestMybatis testMybatis);
}
创建mapper xml my-mapper.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">
<!-- 注意:这里namespace必须使用mapper接口的类全限定名称,不然无法自动生成代理 -->
<mapper namespace="dongshi.daddy.mapper.TestMyBatisMapper">
<insert id="insertTestMybatis" parameterType="dongshi.daddy.TestMybatis">
INSERT INTO test_mybatis (
full_name,
age
)
VALUES
(
#{fullName},
#{age}
);
</insert>
</mapper>
上述的namespace和parameterType注意改成自己的。
创建全局配置文件mybatis-config.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>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 配置mybatis自动转换为驼峰式命名 -->
<!-- 环境,可以配置多个,default:指定采用哪个环境 -->
<environments default="test">
<!-- id:唯一标识 -->
<environment id="test">
<!-- 事务管理器,JDBC类型的事务管理器 -->
<transactionManager type="JDBC" />
<!-- 数据源,池类型的数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3366/test" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="dongshi/daddy/mapper/my-mapper.xml"/>
</mappers>
</configuration>
测试代码:
public class TestMyBatisMapperTest {
private TestMyBatisMapper testMyBatisMapper;
private SqlSession sqlSession;
@Before
public void setUp() throws Exception {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
this.sqlSession = sqlSession;
this.testMyBatisMapper = sqlSession.getMapper(TestMyBatisMapper.class);
}
@Test
public void insertTestMyBatis() {
TestMybatis newTestMyBatis = new TestMybatis(1212, "别吵吵,只有中国人解放军才能保卫台湾", 33);
// newTestMyBatis.setFullName(UUID.randomUUID().toString());
// newTestMyBatis.setAge(new Random().nextInt(100));
testMyBatisMapper.insertTestMybatis(newTestMyBatis);
this.sqlSession.commit();
}
}
运行后:
截止到这里我们看下JDBC,JPA,ORM框架之间的关系:
另外,spring针对常见数据存储组件提供了spring-data 项目 ,如下图:
我们来看下spring-data-jpa是如何操作数据库的
3.3:spring data jpa
spring data jpa 进一步进一步封装了hibernate,其和JDBC,ORM等关系如下图:
提供了一套新的规范,来进一步的减少开发的工作量,部分规范,如下图:
即通过方法命名来直接映射为特定的sql语句,下面我们详细看下。
- 创建表
CREATE TABLE `jpa_user` (
`id` bigint(64) NOT NULL AUTO_INCREMENT,
`name` varchar(63) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4
- xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.10.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--spring-data-jpa-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.23</version>
</dependency>
<!--oracle桥接器-->
<!--
<dependency>
<groupId>com.oracle.ojdbc</groupId>
<artifactId>ojdbc8</artifactId>
<scope>runtime</scope>
</dependency>
-->
<dependency>
<!--
<groupId>mysql</groupId>
<artifactId>mysql</artifactId>
<version>5</version>
<systemPath>${project.basedir}/jar/mysql-connector-java-5.1.47.jar</systemPath>
-->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
- 表对应实体
@Data
@Entity
@Table(name = "jpa_user")
//@EntityListeners(AuditingEntityListener.class)
public class JpaUser {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "JPA_USER_S")
@SequenceGenerator(sequenceName = "JPA_USER_S", name = "JPA_USER_S", allocationSize = 1)
private Long id;
@Column(name = "name")
private String name;
}
- yml配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3366/test
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
jpa:
hibernate:
ddl-auto: update #自动更新
show-sql: true #日志中显示sql语句
application:
name: spring-data-jpa-demo
server:
port: 2333 #端口号
- repository service controller
public interface JpaUserRepository extends JpaRepository<JpaUser, Long> {
}
public interface JpaUserService {
/**
* 新增用户
* @param user 用户对象
*/
JpaUser insertUser(JpaUser user);
}
@Service
public class JpaUserServiceImpl implements JpaUserService {
@Resource
private JpaUserRepository jpaUserRepository;
@Override
public JpaUser insertUser(JpaUser user) {
return jpaUserRepository.save(user);
}
}
@RestController
@RequestMapping("/user")
public class JpaUserController {
@Resource
private JpaUserService jpaUserService;
@PostMapping("/addUser")
public JpaUser addUser(@RequestBody JpaUser user){
return jpaUserService.insertUser(user);
}
}
- main
/**
* 启动类
*/
//@EnableJpaAuditing
@SpringBootApplication
public class SpringContextApplication {
public static void main(String[] args) {
SpringApplication.run(SpringContextApplication.class, args);
}
}
- 启动访问
console输出:
2023-07-05 16:52:57.115 INFO 14556 --- [nio-2333-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 4 ms
Hibernate: select jpauser0_.id as id1_0_0_, jpauser0_.name as name2_0_0_ from jpa_user jpauser0_ where jpauser0_.id=?
Hibernate: select next_val as id_val from jpa_user_s for update
Hibernate: update jpa_user_s set next_val= ? where next_val=?
Hibernate: insert into jpa_user (name, id) values (?, ?)
4:其他
4.1:spring jdbc
封装了JDBC,提供了简单的数据库操作,我们经常用到的JdbcTemplate就在此功能内,如下图:
看下其如何使用。
- 表
CREATE TABLE `user` (
`id` int(11) NOT NULL,
`user_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_account` (`user_name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
- pom
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.10.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
- dao
@Repository
public class UserDao {
@Resource
private JdbcTemplate jdbcTemplate;
public void addUser() {
String insertSql = "INSERT INTO `user`(id, `user_name`) VALUES (?, ?) ";
jdbcTemplate.update(insertSql, new Object[]{ 999, "精确治理是个啥?" });
}
}
- main
注意实现了ApplicationRunner接口,我们在其run方法中直接插入数据了。
@SpringBootApplication
public class SpringJdbcApplication implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(SpringJdbcApplication.class, args);
}
@Resource
private UserDao userDao;
@Override
public void run(ApplicationArguments args) throws Exception {
userDao.addUser();
}
}
运行:
4.2:事务
针对事务管理,spring定义了接口PlatformTransactionManager,针对不同的操作数据库的方式提供了不同的事务管理类,如对JDBC提供了DataSourceTransactionManager,Hibernate提供了HibernateTransactionManager,是通过AOP的方式来实现的。
写在后面
参考文章列表
JDBC连接Mysql数据库详解 。
JDBC连接Mysql数据库详解 。
学习笔记之JPA连接数据库 。
JPA、Hibernate、Spring data jpa 之间的关系,终于明白了 。
Java获取类、方法、属性上的注解 。
mybatis学习文章系列(偏源码) 。
最详细的Spring-data-jpa入门(一) 。
Spring Boot(三): 操作数据库-Spring JDBC 。