写在前面
本文看下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 。



















