那些年背过的题:Spring jdbc设计与实现

Spring JDBC 是 Spring Framework 提供的一个模块,用于简化与关系数据库交互的工作。它提供了对 JDBC API 的封装,使得使用 JDBC 更为简便,同时减少了编写原始 JDBC 代码时需要处理的样板代码和异常处理逻辑。

Spring JDBC 的设计目标

  1. 简化数据库操作:通过模板方法模式,减少重复的 JDBC 代码。
  2. 自动资源管理:自动处理 ConnectionStatement 和 ResultSet 的资源释放,避免资源泄漏。
  3. 统一异常转换:将原生 SQL 异常转换为一致的 Spring DataAccessException 层次结构。
  4. 提供更高层次的抽象:例如 JdbcTemplate 和 NamedParameterJdbcTemplate,使得执行 SQL 语句更加方便。

核心组件

  1. JdbcTemplate: JdbcTemplate 是 Spring JDBC 的核心类,它封装了基本的 JDBC 操作,如查询、插入、更新和删除。通过 JdbcTemplate,开发者可以避免手动管理资源和处理异常。

     

    java

    代码解读

    复制代码

    @Autowired private JdbcTemplate jdbcTemplate; public List<User> findAllUsers() { String sql = "SELECT * FROM users"; return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class)); }
  2. DataSource: DataSource 是一个标准的接口,用于获取数据库连接。Spring 提供了多种 DataSource 实现,例如 DriverManagerDataSource 和 DataSource 配置在应用服务器上如 Tomcat 或 JNDI。

    使用 Apache Commons DBCP:

    整理了这份Java面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

    需要全套面试笔记【点击此处】即可免费获取

    xml

    代码解读

    复制代码

    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/mydb" /> <property name="username" value="root" /> <property name="password" value="password" /> </bean>
  3. NamedParameterJdbcTemplate: NamedParameterJdbcTemplate 是 JdbcTemplate 的扩展,允许使用具名参数而不是传统的 ? 占位符,使得 SQL 语句更具可读性。

     

    java

    代码解读

    复制代码

    @Autowired private NamedParameterJdbcTemplate namedParameterJdbcTemplate; public void updateUser(User user) { String sql = "UPDATE users SET name = :name WHERE id = :id"; Map<String, Object> params = new HashMap<>(); params.put("name", user.getName()); params.put("id", user.getId()); namedParameterJdbcTemplate.update(sql, params); }
  4. RowMapper: RowMapper 用于将结果集中的每一行映射到 Java 对象。Spring 提供了多个内置的实现,例如 BeanPropertyRowMapper,也可以自定义 RowMapper

     

    java

    代码解读

    复制代码

    public class UserRowMapper implements RowMapper<User> { @Override public User mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User(); user.setId(rs.getInt("id")); user.setName(rs.getString("name")); return user; } } // 使用自定义的RowMapper public List<User> findAllUsers() { String sql = "SELECT * FROM users"; return jdbcTemplate.query(sql, new UserRowMapper()); }

使用示例

假设有一个用户表 users,包含字段 id 和 name

  1. 配置数据源和 JdbcTemplate

     

    xml

    代码解读

    复制代码

    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mydb"/> <property name="username" value="root"/> <property name="password" value="password"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> </beans>
  2. 使用 JdbcTemplate 进行数据库操作

     

    java

    代码解读

    复制代码

    @Service public class UserService { @Autowired private JdbcTemplate jdbcTemplate; // 添加用户 public void addUser(User user) { String sql = "INSERT INTO users (name) VALUES (?)"; jdbcTemplate.update(sql, user.getName()); } // 查询所有用户 public List<User> findAllUsers() { String sql = "SELECT * FROM users"; return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class)); } // 根据ID查询用户 public User findUserById(int id) { String sql = "SELECT * FROM users WHERE id = ?"; return jdbcTemplate.queryForObject(sql, new Object[]{id}, new BeanPropertyRowMapper<>(User.class)); } // 更新用户 public void updateUser(User user) { String sql = "UPDATE users SET name = ? WHERE id = ?"; jdbcTemplate.update(sql, user.getName(), user.getId()); } // 删除用户 public void deleteUser(int id) { String sql = "DELETE FROM users WHERE id = ?"; jdbcTemplate.update(sql, id); } }
  3. 实体类 User

     

    java

    代码解读

    复制代码

    public class User { private int id; private String name; // Getters and Setters public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }

总结

Spring JDBC 通过提供模板类 (JdbcTemplate 和 NamedParameterJdbcTemplate) 大大简化了与数据库交互的过程。这些模板类不仅减少了重复的样板代码,还提供了对资源管理和异常处理的自动化支持。同时,通过 RowMapper 接口,Spring JDBC 提供了一种灵活的方式将结果集映射到 Java 对象,使得数据库查询结果处理更加便捷。

JdbcTemplate核心源码实现流程

以 queryForObject 方法为例,这是一个常见的用于执行查询并返回单个结果的方法:

 

java

代码解读

复制代码

public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args) throws DataAccessException { return query(sql, new ResultSetExtractor<T>() { @Override public T extractData(ResultSet rs) throws SQLException, DataAccessException { if (!rs.next()) { throw new EmptyResultDataAccessException(1); } T result = rowMapper.mapRow(rs, 1); if (rs.next()) { throw new IncorrectResultSizeDataAccessException(1, 2); } return result; } }, args); }

上述代码展示了一个 queryForObject 方法的实现,其中使用了 query 方法来实际执行 SQL 查询。

query 方法的核心实现

让我们深入到 query 方法的内部实现流程:

 

java

代码解读

复制代码

public <T> T query(String sql, ResultSetExtractor<T> rse, Object... args) throws DataAccessException { Assert.notNull(sql, "SQL must not be null"); Assert.notNull(rse, "ResultSetExtractor must not be null"); class QueryStatementCallback implements PreparedStatementCallback<T>, SqlProvider { @Override public T doInPreparedStatement(PreparedStatement ps) throws SQLException { ResultSet rs = null; try { applyStatementSettings(ps); rs = ps.executeQuery(); return rse.extractData(rs); } finally { JdbcUtils.closeResultSet(rs); } } @Override public String getSql() { return sql; } } return execute(new SimplePreparedStatementCreator(sql), new QueryStatementCallback()); }

execute 方法

execute 方法是 JdbcTemplate 执行核心逻辑的地方,它负责创建和管理数据库连接、预编译 SQL 语句、执行 SQL 以及处理资源释放。

 

java

代码解读

复制代码

public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action) throws DataAccessException { Assert.notNull(psc, "PreparedStatementCreator must not be null"); Assert.notNull(action, "PreparedStatementCallback must not be null"); Connection con = DataSourceUtils.getConnection(getDataSource()); PreparedStatement ps = null; try { ps = psc.createPreparedStatement(con); applyStatementSettings(ps); T result = action.doInPreparedStatement(ps); handleWarnings(ps); return result; } catch (SQLException ex) { // Exception translation throw translateException("PreparedStatementCallback", getSql(action), ex); } finally { JdbcUtils.closeStatement(ps); DataSourceUtils.releaseConnection(con, getDataSource()); } }

关键步骤解析

  1. 获取数据库连接: 使用 DataSourceUtils.getConnection 从数据源获取连接。这一步通常会涉及从连接池中获取连接。
  2. 创建 PreparedStatement: 调用 PreparedStatementCreator.createPreparedStatement 来创建 PreparedStatement 对象。
  3. 应用 Statement 设置: 调用 applyStatementSettings 配置 PreparedStatement(例如超时设置)。
  4. 执行 SQL 语句: 调用 PreparedStatementCallback.doInPreparedStatement 执行 SQL 语句。在这里,传入的 ResultSetExtractor 会处理 ResultSet 并返回结果。
  5. 处理 SQL 警告: 调用 handleWarnings 方法来处理可能出现的 SQL 警告。
  6. 异常处理: 捕获 SQLException 并通过 translateException 方法将其转换为 Spring 的 DataAccessException
  7. 资源清理: 在 finally 块中关闭 PreparedStatement 和释放数据库连接,确保资源不会泄漏。

总结

JdbcTemplate 的核心设计模式是模板方法模式,它通过抽象和回调的方式简化了 JDBC 操作。通过 execute 方法,JdbcTemplate 管理了数据库连接、SQL 执行和资源清理等复杂任务,使得开发者可以专注于业务逻辑而不必关心底层的 JDBC 细节。

Hibernate 底层如何实现将实体类映射成数据库表

以下是一个完整的概述,展示了从配置到执行的详细过程。

1. 配置与初始化

配置文件(hibernate.cfg.xml 或 hibernate.properties

Hibernate 的配置文件定义了数据库连接信息、Hibernate 属性和实体类映射信息。

 

xml

代码解读

复制代码

<hibernate-configuration> <session-factory> <!-- Database connection settings --> <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mydb</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">password</property> <!-- Dialect --> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <!-- Echo SQL to stdout --> <property name="hibernate.show_sql">true</property> <!-- Create the database schema on startup --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- List of annotated classes --> <mapping class="com.example.model.User"/> </session-factory> </hibernate-configuration>

2. 元数据解析

注解解析

Hibernate 使用 JPA 注解来解析实体类和它们之间的关系。这些注解提供了映射信息,将类和字段映射到数据库的表和列。

 

java

代码解读

复制代码

@Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "name", nullable = false) private String name; @Column(name = "email", nullable = false, unique = true) private String email; // Getters and Setters }

XML 映射解析

XML 文件也可以用于定义映射信息,这种方式在某些情况下可能比注解更加灵活。

 

xml

代码解读

复制代码

<class name="com.example.model.User" table="users"> <id name="id" column="id"> <generator class="identity"/> </id> <property name="name" column="name" not-null="true"/> <property name="email" column="email" not-null="true" unique="true"/> </class>

3. 配置处理与会话工厂建立

Configuration 类

该类负责加载配置文件和映射信息,并构建 SessionFactorySessionFactory 是一个重量级对象,通常在应用程序启动时创建一次。

 

java

代码解读

复制代码

Configuration configuration = new Configuration().configure(); SessionFactory sessionFactory = configuration.buildSessionFactory();

4. 实体类和表的映射

元数据处理器

通过 MetadataSources 和 MetadataBuilder 配置和解析元数据。

 

java

代码解读

复制代码

ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() .configure("hibernate.cfg.xml").build(); MetadataSources metadataSources = new MetadataSources(serviceRegistry); metadataSources.addAnnotatedClass(User.class); Metadata metadata = metadataSources.buildMetadata(); SessionFactory sessionFactory = metadata.buildSessionFactory();

5. SQL 生成与执行

Hibernate 方言

方言(Dialect)类生成特定于数据库类型的 SQL 语句。例如,MySQL 方言会生成 MySQL 特有的 SQL。

HQL 和 Criteria API

Hibernate 提供了 HQL 和 Criteria API,用于构建查询并将其转换为 SQL。

 

java

代码解读

复制代码

// HQL 示例 String hql = "FROM User WHERE email = :email"; Query<User> query = session.createQuery(hql, User.class); query.setParameter("email", email); List<User> results = query.list();

SQL 生成器

Hibernate 从实体元数据中提取表名、列名、主键等信息,生成相应的 SQL 语句。

 

java

代码解读

复制代码

String sql = "SELECT * FROM users WHERE email = ?"; PreparedStatement stmt = connection.prepareStatement(sql); stmt.setString(1, email); ResultSet rs = stmt.executeQuery();

6. 持久化上下文与缓存

一级缓存

每个 Hibernate 会话(Session)都有一级缓存,用于存储当前事务中的持久化对象,以减少重复查询。

 

java

代码解读

复制代码

Session session = sessionFactory.openSession(); User user = session.get(User.class, 1L); // 第一次访问,从数据库加载 User sameUser = session.get(User.class, 1L); // 第二次访问,从缓存加载

二级缓存

二级缓存是全局缓存,可以跨多个会话共享,以提高读取性能。Hibernate 支持集成各种二级缓存提供者,如 Ehcache、Infinispan 等。

 

xml

代码解读

复制代码

<property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

在实体类上启用二级缓存:

 

java

代码解读

复制代码

@Entity @Cacheable @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) @Table(name = "users") public class User { // ... }

7. 脏检查机制

Hibernate 使用脏检查机制(Dirty Checking)来自动检测实体对象的状态变化,并在事务提交时同步这些变化到数据库。这个机制可以确保持久化上下文中的所有更改都能自动反映到数据库中,而不需要显式地调用 save or update 方法。

 

java

代码解读

复制代码

Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); User user = session.get(User.class, 1L); user.setName("New Name"); // 修改对象 tx.commit(); // 提交事务时,Hibernate 自动检测并更新数据库 session.close();

8. 延迟加载

Hibernate 支持延迟加载,即在实际需要数据时才从数据库加载。这对于优化性能非常有用,尤其是在处理大型数据集时。

通过配置实体关联的 FetchType 来控制延迟加载:

 

java

代码解读

复制代码

@Entity @Table(name = "orders") public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; // Getters and Setters }

9. 事务管理

Hibernate 通常与 Java 事务 API (JTA) 或 Spring 事务管理集成,以管理事务的边界。例如,使用 JTA 可以使同一事务涉及多个资源(如数据库和消息队列)。

 

java

代码解读

复制代码

UserTransaction txn = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction"); txn.begin(); Session session = sessionFactory.getCurrentSession(); // 执行持久化操作 txn.commit();

在 Spring 中,可以通过注解方式进行事务管理:

 

java

代码解读

复制代码

@Service public class UserService { @Autowired private SessionFactory sessionFactory; @Transactional public void createUser(User user) { Session session = sessionFactory.getCurrentSession(); session.save(user); } }

10. 实现总结

以下步骤总结了 Hibernate 将实体类映射成数据库表的核心过程:

  1. 配置初始化: 通过 hibernate.cfg.xml 或 hibernate.properties 文件配置数据库连接信息和映射类。
  2. 元数据解析: Hibernate 解析 JPA 注解或 XML 映射文件,生成元数据模型。
  3. 构建 SessionFactory: 使用 Configuration 和 ServiceRegistry 配置并构建 SessionFactory,这是一个重量级对象,通常在应用启动时创建一次。
  4. SQL 生成与执行: 基于方言(Dialect)生成特定数据库的 SQL 语句,并通过 JDBC 执行这些语句。
  5. 缓存机制: 使用一级缓存和可选的二级缓存来优化性能,减少数据库查询。
  6. 脏检查和自动同步: 使用脏检查机制自动检测实体对象的状态变化,并在事务提交时同步到数据库。
  7. 延迟加载和事务管理: 支持延迟加载和强大的事务管理机制,确保数据的一致性和完整性。

通过以上步骤和机制,Hibernate 实现了将 Java 实体类高效地映射成数据库表,同时提供了丰富的功能和灵活性来满足复杂业务场景的需求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值