SSM從入門到實戰:1.6 Spring數據訪問與JDBC模板

👋 大家好,我是 阿問學長!專注于分享優質開源項目解析、畢業設計項目指導支持、幼小初高教輔資料推薦等,歡迎關注交流!🚀

06-Spring數據訪問與JDBC模板

📖 本文概述

本文是SSM框架系列Spring進階篇的第三篇,將深入探討Spring的數據訪問技術,重點介紹JdbcTemplate的使用和最佳實踐。通過詳細的代碼示例,幫助讀者掌握Spring數據訪問層的設計和實現。

🎯 學習目標

  • 深入理解Spring數據訪問架構
  • 掌握JdbcTemplate的各種用法
  • 學會處理數據訪問異常
  • 了解數據源配置和連接池管理
  • 掌握數據訪問層的最佳實踐

1. Spring數據訪問架構

1.1 數據訪問層架構

/*** Spring數據訪問層架構演示*/
public class DataAccessArchitecture {/*** Spring數據訪問層的核心組件:* * 1. DataSource - 數據源,管理數據庫連接* 2. JdbcTemplate - JDBC模板,簡化JDBC操作* 3. NamedParameterJdbcTemplate - 命名參數JDBC模板* 4. SimpleJdbcInsert - 簡化插入操作* 5. SimpleJdbcCall - 簡化存儲過程調用* 6. DataAccessException - 數據訪問異常體系*//*** 數據訪問層的層次結構:* * Service Layer (業務層)*     ↓* DAO Layer (數據訪問層)*     ↓* JdbcTemplate (JDBC模板)*     ↓* DataSource (數據源)*     ↓* Database (數據庫)*//*** Spring數據訪問的優勢:* * 1. 統一的異常體系* 2. 資源管理自動化* 3. 事務管理集成* 4. 模板方法模式簡化代碼* 5. 支持多種數據訪問技術*/
}

1.2 數據源配置

/*** 數據源配置示例*/
@Configuration
public class DataSourceConfig {/*** HikariCP數據源配置(推薦)*/@Bean@Primarypublic DataSource hikariDataSource() {HikariConfig config = new HikariConfig();// 基本連接信息config.setJdbcUrl("jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC");config.setUsername("root");config.setPassword("password");config.setDriverClassName("com.mysql.cj.jdbc.Driver");// 連接池配置config.setMaximumPoolSize(20);              // 最大連接數config.setMinimumIdle(5);                   // 最小空閑連接數config.setConnectionTimeout(30000);         // 連接超時時間(毫秒)config.setIdleTimeout(600000);              // 空閑超時時間(毫秒)config.setMaxLifetime(1800000);             // 連接最大生存時間(毫秒)config.setLeakDetectionThreshold(60000);    // 連接泄漏檢測閾值// 連接測試config.setConnectionTestQuery("SELECT 1");// 連接池名稱config.setPoolName("HikariCP-Pool");return new HikariDataSource(config);}/*** Druid數據源配置*/@Beanpublic DataSource druidDataSource() {DruidDataSource dataSource = new DruidDataSource();// 基本連接信息dataSource.setUrl("jdbc:mysql://localhost:3306/testdb");dataSource.setUsername("root");dataSource.setPassword("password");dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");// 連接池配置dataSource.setInitialSize(5);               // 初始連接數dataSource.setMinIdle(5);                   // 最小空閑連接數dataSource.setMaxActive(20);                // 最大連接數dataSource.setMaxWait(60000);               // 獲取連接最大等待時間// 連接有效性檢測dataSource.setTestWhileIdle(true);dataSource.setTestOnBorrow(false);dataSource.setTestOnReturn(false);dataSource.setValidationQuery("SELECT 1");dataSource.setValidationQueryTimeout(30);// 連接回收dataSource.setTimeBetweenEvictionRunsMillis(60000);dataSource.setMinEvictableIdleTimeMillis(300000);// 監控配置dataSource.setFilters("stat,wall,log4j2");return dataSource;}/*** 嵌入式數據源(用于測試)*/@Bean@Profile("test")public DataSource embeddedDataSource() {return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).addScript("classpath:schema.sql").addScript("classpath:data.sql").build();}/*** JNDI數據源*/@Bean@Profile("production")public DataSource jndiDataSource() {JndiDataSourceLookup lookup = new JndiDataSourceLookup();return lookup.getDataSource("java:comp/env/jdbc/MyDataSource");}
}

2. JdbcTemplate基礎用法

2.1 基本CRUD操作

/*** JdbcTemplate基本CRUD操作*/
@Repository
public class UserDaoImpl implements UserDao {@Autowiredprivate JdbcTemplate jdbcTemplate;/*** 插入操作*/@Overridepublic int save(User user) {String sql = "INSERT INTO users (username, email, age, create_time) VALUES (?, ?, ?, ?)";return jdbcTemplate.update(sql, user.getUsername(), user.getEmail(), user.getAge(), new Timestamp(System.currentTimeMillis()));}/*** 插入并返回自增主鍵*/@Overridepublic User saveAndReturnKey(User user) {String sql = "INSERT INTO users (username, email, age, create_time) VALUES (?, ?, ?, ?)";KeyHolder keyHolder = new GeneratedKeyHolder();jdbcTemplate.update(connection -> {PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);ps.setString(1, user.getUsername());ps.setString(2, user.getEmail());ps.setInt(3, user.getAge());ps.setTimestamp(4, new Timestamp(System.currentTimeMillis()));return ps;}, keyHolder);user.setId(keyHolder.getKey().longValue());return user;}/*** 更新操作*/@Overridepublic int update(User user) {String sql = "UPDATE users SET username = ?, email = ?, age = ?, update_time = ? WHERE id = ?";return jdbcTemplate.update(sql,user.getUsername(),user.getEmail(),user.getAge(),new Timestamp(System.currentTimeMillis()),user.getId());}/*** 刪除操作*/@Overridepublic int deleteById(Long id) {String sql = "DELETE FROM users WHERE id = ?";return jdbcTemplate.update(sql, id);}/*** 批量刪除*/@Overridepublic int[] deleteByIds(List<Long> ids) {String sql = "DELETE FROM users WHERE id = ?";List<Object[]> batchArgs = ids.stream().map(id -> new Object[]{id}).collect(Collectors.toList());return jdbcTemplate.batchUpdate(sql, batchArgs);}/*** 查詢單個對象*/@Overridepublic User findById(Long id) {String sql = "SELECT id, username, email, age, create_time, update_time FROM users WHERE id = ?";try {return jdbcTemplate.queryForObject(sql, new UserRowMapper(), id);} catch (EmptyResultDataAccessException e) {return null; // 沒有找到記錄}}/*** 查詢列表*/@Overridepublic List<User> findAll() {String sql = "SELECT id, username, email, age, create_time, update_time FROM users ORDER BY id";return jdbcTemplate.query(sql, new UserRowMapper());}/*** 條件查詢*/@Overridepublic List<User> findByAge(int minAge, int maxAge) {String sql = "SELECT id, username, email, age, create_time, update_time FROM users WHERE age BETWEEN ? AND ?";return jdbcTemplate.query(sql, new UserRowMapper(), minAge, maxAge);}/*** 統計查詢*/@Overridepublic long count() {String sql = "SELECT COUNT(*) FROM users";return jdbcTemplate.queryForObject(sql, Long.class);}/*** 檢查存在性*/@Overridepublic boolean existsById(Long id) {String sql = "SELECT COUNT(*) FROM users WHERE id = ?";Integer count = jdbcTemplate.queryForObject(sql, Integer.class, id);return count != null && count > 0;}
}

2.2 RowMapper實現

/*** 用戶行映射器*/
public class UserRowMapper implements RowMapper<User> {@Overridepublic User mapRow(ResultSet rs, int rowNum) throws SQLException {User user = new User();user.setId(rs.getLong("id"));user.setUsername(rs.getString("username"));user.setEmail(rs.getString("email"));user.setAge(rs.getInt("age"));user.setCreateTime(rs.getTimestamp("create_time"));user.setUpdateTime(rs.getTimestamp("update_time"));return user;}
}/*** 使用Lambda表達式的RowMapper*/
@Repository
public class LambdaUserDao {@Autowiredprivate JdbcTemplate jdbcTemplate;/*** 使用Lambda表達式簡化RowMapper*/public List<User> findAllWithLambda() {String sql = "SELECT id, username, email, age, create_time, update_time FROM users";return jdbcTemplate.query(sql, (rs, rowNum) -> {User user = new User();user.setId(rs.getLong("id"));user.setUsername(rs.getString("username"));user.setEmail(rs.getString("email"));user.setAge(rs.getInt("age"));user.setCreateTime(rs.getTimestamp("create_time"));user.setUpdateTime(rs.getTimestamp("update_time"));return user;});}/*** 使用BeanPropertyRowMapper*/public List<User> findAllWithBeanMapper() {String sql = "SELECT id, username, email, age, create_time as createTime, update_time as updateTime FROM users";return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));}
}

3. NamedParameterJdbcTemplate

3.1 命名參數的使用

/*** NamedParameterJdbcTemplate使用示例*/
@Repository
public class NamedParameterUserDao {@Autowiredprivate NamedParameterJdbcTemplate namedParameterJdbcTemplate;/*** 使用Map傳遞參數*/public User findByUsernameAndEmail(String username, String email) {String sql = "SELECT * FROM users WHERE username = :username AND email = :email";Map<String, Object> params = new HashMap<>();params.put("username", username);params.put("email", email);try {return namedParameterJdbcTemplate.queryForObject(sql, params, new UserRowMapper());} catch (EmptyResultDataAccessException e) {return null;}}/*** 使用SqlParameterSource*/public int saveWithParameterSource(User user) {String sql = "INSERT INTO users (username, email, age, create_time) " +"VALUES (:username, :email, :age, :createTime)";SqlParameterSource params = new BeanPropertySqlParameterSource(user);return namedParameterJdbcTemplate.update(sql, params);}/*** 使用MapSqlParameterSource*/public List<User> findByAgeRange(int minAge, int maxAge) {String sql = "SELECT * FROM users WHERE age BETWEEN :minAge AND :maxAge ORDER BY age";MapSqlParameterSource params = new MapSqlParameterSource();params.addValue("minAge", minAge);params.addValue("maxAge", maxAge);return namedParameterJdbcTemplate.query(sql, params, new UserRowMapper());}/*** IN查詢*/public List<User> findByIds(List<Long> ids) {String sql = "SELECT * FROM users WHERE id IN (:ids)";Map<String, Object> params = Collections.singletonMap("ids", ids);return namedParameterJdbcTemplate.query(sql, params, new UserRowMapper());}/*** 批量操作*/public int[] batchSave(List<User> users) {String sql = "INSERT INTO users (username, email, age, create_time) " +"VALUES (:username, :email, :age, :createTime)";SqlParameterSource[] batchParams = users.stream().map(BeanPropertySqlParameterSource::new).toArray(SqlParameterSource[]::new);return namedParameterJdbcTemplate.batchUpdate(sql, batchParams);}/*** 復雜查詢條件*/public List<User> findByComplexConditions(UserSearchCriteria criteria) {StringBuilder sql = new StringBuilder("SELECT * FROM users WHERE 1=1");MapSqlParameterSource params = new MapSqlParameterSource();if (criteria.getUsername() != null) {sql.append(" AND username LIKE :username");params.addValue("username", "%" + criteria.getUsername() + "%");}if (criteria.getEmail() != null) {sql.append(" AND email = :email");params.addValue("email", criteria.getEmail());}if (criteria.getMinAge() != null) {sql.append(" AND age >= :minAge");params.addValue("minAge", criteria.getMinAge());}if (criteria.getMaxAge() != null) {sql.append(" AND age <= :maxAge");params.addValue("maxAge", criteria.getMaxAge());}sql.append(" ORDER BY id");return namedParameterJdbcTemplate.query(sql.toString(), params, new UserRowMapper());}
}/*** 搜索條件類*/
public class UserSearchCriteria {private String username;private String email;private Integer minAge;private Integer maxAge;// getter/setter方法
}

4. SimpleJdbcInsert和SimpleJdbcCall

4.1 SimpleJdbcInsert使用

/*** SimpleJdbcInsert使用示例*/
@Repository
public class SimpleJdbcInsertUserDao {private SimpleJdbcInsert simpleJdbcInsert;@Autowiredpublic SimpleJdbcInsertUserDao(DataSource dataSource) {this.simpleJdbcInsert = new SimpleJdbcInsert(dataSource).withTableName("users").usingGeneratedKeyColumns("id");}/*** 簡單插入操作*/public Long save(User user) {Map<String, Object> parameters = new HashMap<>();parameters.put("username", user.getUsername());parameters.put("email", user.getEmail());parameters.put("age", user.getAge());parameters.put("create_time", new Timestamp(System.currentTimeMillis()));Number key = simpleJdbcInsert.executeAndReturnKey(parameters);return key.longValue();}/*** 使用BeanPropertySqlParameterSource*/public Long saveWithBean(User user) {user.setCreateTime(new Timestamp(System.currentTimeMillis()));SqlParameterSource parameters = new BeanPropertySqlParameterSource(user);Number key = simpleJdbcInsert.executeAndReturnKey(parameters);return key.longValue();}/*** 批量插入*/public void batchSave(List<User> users) {Map<String, Object>[] batchValues = users.stream().map(user -> {Map<String, Object> params = new HashMap<>();params.put("username", user.getUsername());params.put("email", user.getEmail());params.put("age", user.getAge());params.put("create_time", new Timestamp(System.currentTimeMillis()));return params;}).toArray(Map[]::new);simpleJdbcInsert.executeBatch(batchValues);}
}

4.2 SimpleJdbcCall使用

/*** SimpleJdbcCall使用示例*/
@Repository
public class SimpleJdbcCallUserDao {private SimpleJdbcCall getUserByIdCall;private SimpleJdbcCall updateUserAgeCall;@Autowiredpublic SimpleJdbcCallUserDao(DataSource dataSource) {JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);// 配置存儲過程調用this.getUserByIdCall = new SimpleJdbcCall(jdbcTemplate).withProcedureName("get_user_by_id");this.updateUserAgeCall = new SimpleJdbcCall(jdbcTemplate).withProcedureName("update_user_age").declareParameters(new SqlParameter("user_id", Types.BIGINT),new SqlParameter("new_age", Types.INTEGER),new SqlOutParameter("result", Types.INTEGER));}/*** 調用存儲過程查詢用戶*/public User getUserById(Long id) {SqlParameterSource in = new MapSqlParameterSource().addValue("user_id", id);Map<String, Object> result = getUserByIdCall.execute(in);// 處理結果集@SuppressWarnings("unchecked")List<Map<String, Object>> rows = (List<Map<String, Object>>) result.get("#result-set-1");if (rows.isEmpty()) {return null;}Map<String, Object> row = rows.get(0);User user = new User();user.setId(((Number) row.get("id")).longValue());user.setUsername((String) row.get("username"));user.setEmail((String) row.get("email"));user.setAge(((Number) row.get("age")).intValue());return user;}/*** 調用存儲過程更新用戶年齡*/public boolean updateUserAge(Long userId, int newAge) {SqlParameterSource in = new MapSqlParameterSource().addValue("user_id", userId).addValue("new_age", newAge);Map<String, Object> result = updateUserAgeCall.execute(in);Integer returnValue = (Integer) result.get("result");return returnValue != null && returnValue > 0;}/*** 調用函數*/public String getUsernameById(Long id) {SimpleJdbcCall function = new SimpleJdbcCall(new JdbcTemplate(dataSource)).withFunctionName("get_username_by_id").declareParameters(new SqlParameter("user_id", Types.BIGINT),new SqlReturnResultSet("result", new UserRowMapper()));SqlParameterSource in = new MapSqlParameterSource().addValue("user_id", id);String username = function.executeFunction(String.class, in);return username;}
}

5. 異常處理

5.1 數據訪問異常體系

/*** Spring數據訪問異常處理*/
@Repository
public class ExceptionHandlingUserDao {@Autowiredprivate JdbcTemplate jdbcTemplate;private static final Logger logger = LoggerFactory.getLogger(ExceptionHandlingUserDao.class);/*** 處理各種數據訪問異常*/public User saveUser(User user) {String sql = "INSERT INTO users (username, email, age) VALUES (?, ?, ?)";try {KeyHolder keyHolder = new GeneratedKeyHolder();jdbcTemplate.update(connection -> {PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);ps.setString(1, user.getUsername());ps.setString(2, user.getEmail());ps.setInt(3, user.getAge());return ps;}, keyHolder);user.setId(keyHolder.getKey().longValue());return user;} catch (DuplicateKeyException e) {logger.error("用戶名或郵箱已存在: {}", user.getUsername(), e);throw new UserAlreadyExistsException("用戶名或郵箱已存在", e);} catch (DataIntegrityViolationException e) {logger.error("數據完整性約束違反: {}", user.getUsername(), e);throw new InvalidUserDataException("用戶數據不符合約束條件", e);} catch (DataAccessException e) {logger.error("數據訪問異常: {}", user.getUsername(), e);throw new UserDataAccessException("保存用戶時發生數據庫錯誤", e);}}/*** 處理查詢異常*/public User findUserById(Long id) {String sql = "SELECT * FROM users WHERE id = ?";try {return jdbcTemplate.queryForObject(sql, new UserRowMapper(), id);} catch (EmptyResultDataAccessException e) {logger.debug("用戶不存在: {}", id);return null;} catch (IncorrectResultSizeDataAccessException e) {logger.error("查詢返回多個結果,期望單個結果: {}", id, e);throw new DataInconsistencyException("數據不一致,ID對應多個用戶", e);} catch (DataAccessException e) {logger.error("查詢用戶時發生數據庫錯誤: {}", id, e);throw new UserDataAccessException("查詢用戶時發生數據庫錯誤", e);}}/*** 事務回滾異常處理*/@Transactionalpublic void transferUserData(Long fromUserId, Long toUserId) {try {// 模擬數據轉移操作User fromUser = findUserById(fromUserId);User toUser = findUserById(toUserId);if (fromUser == null || toUser == null) {throw new UserNotFoundException("用戶不存在");}// 執行轉移邏輯performDataTransfer(fromUser, toUser);} catch (DataAccessException e) {logger.error("數據轉移失敗,事務將回滾", e);throw new DataTransferException("數據轉移失敗", e);}}private void performDataTransfer(User fromUser, User toUser) {// 模擬數據轉移邏輯}
}/*** 自定義異常類*/
public class UserAlreadyExistsException extends RuntimeException {public UserAlreadyExistsException(String message, Throwable cause) {super(message, cause);}
}public class InvalidUserDataException extends RuntimeException {public InvalidUserDataException(String message, Throwable cause) {super(message, cause);}
}public class UserDataAccessException extends RuntimeException {public UserDataAccessException(String message, Throwable cause) {super(message, cause);}
}public class DataInconsistencyException extends RuntimeException {public DataInconsistencyException(String message, Throwable cause) {super(message, cause);}
}public class UserNotFoundException extends RuntimeException {public UserNotFoundException(String message) {super(message);}
}public class DataTransferException extends RuntimeException {public DataTransferException(String message, Throwable cause) {super(message, cause);}
}

6. 最佳實踐

6.1 DAO層設計模式

/*** DAO層設計最佳實踐*/
public interface BaseDao<T, ID> {T save(T entity);T update(T entity);void deleteById(ID id);T findById(ID id);List<T> findAll();long count();boolean existsById(ID id);
}/*** 抽象DAO基類*/
public abstract class AbstractJdbcDao<T, ID> implements BaseDao<T, ID> {@Autowiredprotected JdbcTemplate jdbcTemplate;@Autowiredprotected NamedParameterJdbcTemplate namedParameterJdbcTemplate;protected abstract String getTableName();protected abstract String getIdColumnName();protected abstract RowMapper<T> getRowMapper();protected abstract Map<String, Object> entityToMap(T entity);@Overridepublic T findById(ID id) {String sql = String.format("SELECT * FROM %s WHERE %s = ?", getTableName(), getIdColumnName());try {return jdbcTemplate.queryForObject(sql, getRowMapper(), id);} catch (EmptyResultDataAccessException e) {return null;}}@Overridepublic List<T> findAll() {String sql = String.format("SELECT * FROM %s ORDER BY %s", getTableName(), getIdColumnName());return jdbcTemplate.query(sql, getRowMapper());}@Overridepublic long count() {String sql = String.format("SELECT COUNT(*) FROM %s", getTableName());return jdbcTemplate.queryForObject(sql, Long.class);}@Overridepublic boolean existsById(ID id) {String sql = String.format("SELECT COUNT(*) FROM %s WHERE %s = ?", getTableName(), getIdColumnName());Integer count = jdbcTemplate.queryForObject(sql, Integer.class, id);return count != null && count > 0;}
}/*** 具體DAO實現*/
@Repository
public class UserDaoImpl extends AbstractJdbcDao<User, Long> implements UserDao {@Overrideprotected String getTableName() {return "users";}@Overrideprotected String getIdColumnName() {return "id";}@Overrideprotected RowMapper<User> getRowMapper() {return new UserRowMapper();}@Overrideprotected Map<String, Object> entityToMap(User user) {Map<String, Object> map = new HashMap<>();map.put("username", user.getUsername());map.put("email", user.getEmail());map.put("age", user.getAge());map.put("create_time", user.getCreateTime());map.put("update_time", user.getUpdateTime());return map;}@Overridepublic User save(User user) {if (user.getId() == null) {return insert(user);} else {update(user);return user;}}private User insert(User user) {String sql = "INSERT INTO users (username, email, age, create_time) VALUES (?, ?, ?, ?)";KeyHolder keyHolder = new GeneratedKeyHolder();jdbcTemplate.update(connection -> {PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);ps.setString(1, user.getUsername());ps.setString(2, user.getEmail());ps.setInt(3, user.getAge());ps.setTimestamp(4, new Timestamp(System.currentTimeMillis()));return ps;}, keyHolder);user.setId(keyHolder.getKey().longValue());return user;}@Overridepublic User update(User user) {String sql = "UPDATE users SET username = ?, email = ?, age = ?, update_time = ? WHERE id = ?";int rows = jdbcTemplate.update(sql,user.getUsername(),user.getEmail(),user.getAge(),new Timestamp(System.currentTimeMillis()),user.getId());if (rows == 0) {throw new UserNotFoundException("用戶不存在: " + user.getId());}return user;}@Overridepublic void deleteById(Long id) {String sql = "DELETE FROM users WHERE id = ?";int rows = jdbcTemplate.update(sql, id);if (rows == 0) {throw new UserNotFoundException("用戶不存在: " + id);}}/*** 自定義查詢方法*/public List<User> findByUsername(String username) {String sql = "SELECT * FROM users WHERE username LIKE ?";return jdbcTemplate.query(sql, getRowMapper(), "%" + username + "%");}public List<User> findByAgeRange(int minAge, int maxAge) {String sql = "SELECT * FROM users WHERE age BETWEEN ? AND ? ORDER BY age";return jdbcTemplate.query(sql, getRowMapper(), minAge, maxAge);}
}

7. 小結

本文深入介紹了Spring的數據訪問技術:

  1. 數據訪問架構:Spring數據訪問層的核心組件和架構
  2. JdbcTemplate:基本CRUD操作和RowMapper使用
  3. 命名參數模板:NamedParameterJdbcTemplate的各種用法
  4. 簡化操作:SimpleJdbcInsert和SimpleJdbcCall的使用
  5. 異常處理:數據訪問異常體系和處理策略
  6. 最佳實踐:DAO層設計模式和代碼組織

掌握Spring數據訪問的關鍵點:

  • 正確配置數據源和連接池
  • 熟練使用JdbcTemplate進行數據操作
  • 合理處理數據訪問異常
  • 設計清晰的DAO層架構
  • 選擇合適的參數傳遞方式

🔗 下一篇預告

下一篇文章將介紹Spring Web開發基礎,學習如何使用Spring進行Web應用開發。


相關文章:

  • 上一篇:Spring事務管理深入解析
  • [下一篇:Spring Web開發基礎](./Spring Web開發基礎.md)
  • 返回目錄

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/93936.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93936.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93936.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

下一代IT服務管理:ITIL5會是什么樣?

ITIL4發布到現在也就5年多時間&#xff0c;按照以往的更新節奏&#xff0c;ITIL5最早也得2027年之后。但現在IT發展的速度&#xff0c;跟以前完全不是一個量級。AI都快把我們的飯碗搶了&#xff08;開個玩笑&#xff09;&#xff0c;ITIL要是還按部就班&#xff0c;估計真要被時…

最新研究進展:2023-2025年神經機器翻譯突破性成果

文章目錄 一、模型架構創新 1.1 混合架構的崛起 1.2 多模態翻譯的突破 1.3 大語言模型與NMT的深度融合(2023-2024) 1.4 非自回歸翻譯(NAT)的效率革命(2024) 二、數據與訓練策略優化 2.1 低資源語言翻譯的飛躍 2.2 動態數據增強技術 三、效率與部署 3.1 模型壓縮與加速 3.…

OpenTelemetry WebSocket 監控終極方案:打通最后一公里

概述 OpenTelemetry&#xff0c;以下簡稱 OTEL&#xff0c;是由 CNCF 托管的“一站式可觀測性標準”&#xff0c;把指標、鏈路、日志三大信號統一為單一 SDK/API&#xff0c;零侵入地采集從瀏覽器、移動端到后端、容器、云服務的全棧遙測數據&#xff0c;并支持 40 后端一鍵導…

VS Code 出現的 Web 視圖加載錯誤和服務工作者注冊失敗問題解決方案

針對 VS Code 或 Cursor &#xff08;vscode系&#xff09;中出現的 Web 視圖加載錯誤和服務工作者注冊失敗問題&#xff0c;以下是永久性解決方案的完整操作指南&#xff1a;解決方案步驟打開命令面板 使用快捷鍵 CtrlShiftP&#xff08;Windows/Linux&#xff09;或 CmdShift…

【qml-4】qml與c++交互(類型多例)

背景&#xff1a; 【qml-1】qml與c交互第一次嘗試&#xff08;實例注入&#xff09; 【qml-2】嘗試一個有模式的qml彈窗 【qml-3】qml與c交互第二次嘗試&#xff08;類型注冊&#xff09; 【qml-4】qml與c交互&#xff08;類型多例&#xff09; 【qml-5】qml與c交互&#…

圖數據庫如何構筑 Web3 風控防線 聚焦批量注冊與鏈上盜轉 悅數圖數據庫

隨著 Web3 生態的不斷演進&#xff0c;鏈上風險呈現出團伙化、隱蔽化和動態化的趨勢&#xff0c;傳統的單點風控手段已難以應對復雜多變的攻擊模式。尤其在批量注冊薅羊毛與鏈上交易盜轉洗錢等高頻風險場景中&#xff0c;攻擊者往往通過偽造身份、跨鏈操作、多層嵌套轉賬等方式…

恒流源電路學習

恒流源的設計原理&#xff1a; 如圖所示你可以看到右邊的的推到公式得到紅點處的電壓是一個和左邊相關的定值&#xff0c;所以呢右邊的電流就是電壓除以那個4Ω&#xff0c;所以得到右邊的電路的電流大體是一個定值&#xff0c;不管你再加什么東西都可以保持這個電流&#xff…

基于生成對抗網絡的模糊圖像恢復原理與技術實現

1. 引言圖像模糊是數字圖像處理中的常見問題&#xff0c;其成因包括相機抖動、物體運動、聚焦不良等。傳統方法如維納濾波、Lucy-Richardson 算法等依賴于模糊核估計和逆濾波&#xff0c;在復雜場景下性能有限。生成對抗網絡&#xff08;Generative Adversarial Networks, GAN&…

【Doris 系列】Doris IP 變更修復

FE 恢復 異常日志 查看 fe.out 會有以下報錯&#xff0c;此時 fe 進程是無法啟動的&#xff0c;操作前注意備份所有 fe 的元數據并停止上游讀寫動作&#xff01; java.io.IOException: the self host 192.168.31.78 does not equal to the host in ROLE file 192.168.31.81. Yo…

安卓14系統應用收不到開機廣播

安卓14系統應用收不到開機廣播 - Wesley’s Blog 前段時間有測試反饋在安卓14 上面某系統應用恢復出廠設置后沒有自啟動&#xff0c;究竟是什么原因呢&#xff1f; 回顧 Android 從3.1開始&#xff0c;會將新安裝并且從未被啟動的應用置為“STOPPED”狀態&#xff0c;或者被…

C# Attribute 方法擴展

場景 剛寫完一個干凈利落的方法&#xff0c;比如保存數據到數據庫&#xff0c;邏輯清晰、結構優雅&#xff0c; 第二天&#xff0c;“嘿&#xff0c;保存完數據&#xff0c;記得給客戶發個郵件哦~” 第三天&#xff0c;“能不能再發個消息通知其他系統&#xff1f;” 第四天&am…

【URP】[法線貼圖]為什么主要是藍色的?

【從UnityURP開始探索游戲渲染】專欄-直達 法線貼圖呈現藍紫色調&#xff08;尤其以藍色為主&#xff09;是由其?存儲原理、切線空間坐標系設計及顏色編碼規則共同決定的?。 核心原因&#xff1a;法線向量的存儲規則? ?法線向量的物理范圍? 法線是單位向量&#xff0c;…

驅動開發系列63 - NVIDIA 開源GPU驅動open-gpu-kernel-modules編譯調試

目錄 一:通過apt方式安裝nvidia 驅動 二:通過 .run 方式安裝nvidia驅動 三:編譯安裝nvidia開源內核驅動 四:驗證和調試 五:卸載驅動 1. 以apt方式安裝nvidia 驅動的卸載方法 2. 以.run方式安裝nvidia驅動的卸載方法 六:安裝CUDA環境 一:通過apt方式安裝nvidia 驅動…

對KingbaseES架構的解析:從讀寫分離到異地災備的技術實現與保障機制

聲明&#xff1a;文章為本人真實測評博客&#xff0c;非廣告&#xff0c;并沒有推廣該平臺 &#xff0c;為用戶體驗文章 本人旨在分享最真實的用戶體驗&#xff0c;為關注此類產品的朋友們提供一個客觀的參考。 文章目錄一、架構全景&#xff1a;四級高可用構建數字基礎1.1 物…

Visual Studio中的常用調試功能(上)

1、利用斷點進行調試添加斷點的方式有以下幾種1.鍵盤快捷鍵F92.通過菜單【Debug&#xff08;調試&#xff09;】-》【Toggle BreakPoint&#xff08;切換斷點&#xff09;】3.點擊代碼行左邊的空白處&#xff08;推薦&#xff09;設置斷點后&#xff0c;按F5運行程序&#xff0…

Linux -- 線程同步

1.1條件變量 (1)當?個線程互斥地訪問某個變量時&#xff0c;它可能發現在其它線程改變狀態之前&#xff0c;它什么也做不了。 (2)例如?個線程訪問隊列時&#xff0c;發現隊列為空&#xff0c;它只能等待&#xff0c;只到其它線程將?個節點添加到隊列 中。這種情況就需要?到…

前端進階指南:JavaScript性能優化實戰全解析

深入剖析 JavaScript 性能瓶頸&#xff0c;分享優化技巧與最佳實踐&#xff0c;讓你的前端應用更快、更穩、更流暢。 &#x1f4d1; 目錄 一、前言 二、性能瓶頸的常見來源 三、JavaScript代碼優化技巧 1. 避免重復計算 2. 合理使用防抖與節流 3. 使用事件委托 四、渲染…

RabbitMQ:SpringAMQP Direct Exchange(直連型交換機)

目錄一、案例需求二、基礎配置三、代碼實現直連型交換機也叫做定向交換機&#xff0c;通過RoutingKey綁定交換機與隊列直接的關系。 生產者源碼 消費者源碼 一、案例需求 在RabbitMQ控制臺中&#xff0c;聲明隊列direct.queue1和direct.queue2。在RabbitMQ控制臺中&#xff…

implement libtime on Windows

因為Windows的time命令和Linux的time命令不一樣&#xff0c;嘗試實現libtime libtime.h /** libtime.h - 跨平臺時間測量庫* 功能&#xff1a;執行外部命令并測量其運行時間和資源使用*/#ifndef LIBTIME_H #define LIBTIME_H#include <stdio.h> #include <stdlib.h>…

Unity進階--C#補充知識點--【C#各版本的新功能新語法】C#1~4與C#5

來源于唐老獅的視頻教學&#xff0c;僅作記錄和感悟記錄&#xff0c;方便日后復習或者查找 一.C#版本與Unity的關系 1.各Unity版本支持的C#版本 更多信息可以在Untiy官網說明查看 https://docs.unity3d.com/2020.3/Documentation/Manual/CSharpCompiler.html&#xff08;這個好…