Springboot 整合druid+mybatis+jta分布式事务+多数据源aop注解动态切换 (一篇到位)-程序员宅基地

技术标签: 程序员  spring boot  mybatis  分布式  

org.springframework.boot

spring-boot-starter-jta-atomikos

org.projectlombok

lombok

1.16.10

mysql

mysql-connector-java

6.0.6

com.alibaba

druid-spring-boot-starter

1.1.9

org.springframework.boot

spring-boot-starter-aop

org.mybatis.spring.boot

mybatis-spring-boot-starter

2.1.0

org.springframework.boot

spring-boot-starter-test

test

然后是数据源的yml信息,application.yml:

server:

port: 8077

spring:

application:

name: jta-dbsource

datasource:

druid:

mydbone:

url: jdbc:mysql://localhost:3306/mydbone?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&pinGlobalTxToPhysicalConnection=true&autoReconnect=true

username: root

password: root

driver-class-name: com.mysql.cj.jdbc.Driver

初始化时建立物理连接的个数。初始化发生在显示调用 init 方法,或者第一次 getConnection 时

initialSize: 5

最小连接池数量

minIdle: 5

最大连接池数量

maxActive: 10

获取连接时最大等待时间,单位毫秒。配置了 maxWait 之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置 useUnfairLock 属性为 true 使用非公平锁。

maxWait: 60000

Destroy 线程会检测连接的间隔时间,如果连接空闲时间大于等于 minEvictableIdleTimeMillis 则关闭物理连接。

timeBetweenEvictionRunsMillis: 60000

连接保持空闲而不被驱逐的最小时间

minEvictableIdleTimeMillis: 300000

用来检测连接是否有效的 sql 因数据库方言而异, 例如 oracle 应该写成 SELECT 1 FROM DUAL

validationQuery: SELECT 1

建议配置为 true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,执行 validationQuery 检测连接是否有效。

testWhileIdle: true

申请连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。

testOnBorrow: false

归还连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。

testOnReturn: false

是否自动回收超时连接

removeAbandoned: true

超时时间 (以秒数为单位)

remove-abandoned-timeout: 1800

logAbandoned: true

pinGlobalTxToPhysicalConnection: true

mydbtwo:

url: jdbc:mysql://localhost:3306/mydbtwo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&pinGlobalTxToPhysicalConnection=true&autoReconnect=true

username: root

password: root

driver-class-name: com.mysql.cj.jdbc.Driver

initialSize: 6

minIdle: 6

maxActive: 10

maxWait: 60000

timeBetweenEvictionRunsMillis: 60000

minEvictableIdleTimeMillis: 300000

validationQuery: SELECT 1

testWhileIdle: true

testOnBorrow: false

testOnReturn: false

removeAbandoned: true

remove-abandoned-timeout: 1800

logAbandoned: true

pinGlobalTxToPhysicalConnection: true

WebStatFilter 用于采集 web-jdbc 关联监控的数据。

web-stat-filter:

是否开启 WebStatFilter 默认是 true

enabled: true

需要拦截的 url

url-pattern: /*

排除静态资源的请求

exclusions: “.js,.gif,.jpg,.png,.css,.ico,/druid/*”

Druid 内置提供了一个 StatViewServlet 用于展示 Druid 的统计信息。

stat-view-servlet:

#是否启用 StatViewServlet 默认值 true

enabled: true

需要拦截的 url

url-pattern: /druid/*

允许清空统计数据

reset-enable: true

login-username: myname

login-password: mypwd

-----接下来就是代码环节-----

========================

大家多注意看注释,很多关键信息都用注释方式进行了简明的介绍:


先创建一个自定义注解,DataSource.java:

import java.lang.annotation.*;

/**

  • @Author : JCccc

  • @CreateTime : 2019/8/28

  • @Description :

**/

@Documented

@Target({ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

public @interface DataSource {

String value() default DataSourceNames.ONE;

}

然后是创建 DataSourceNames.java,用于简单数据源命名:

/**

  • @Author : JCccc

  • @CreateTime : 2019/8/28

  • @Description :

**/

public interface DataSourceNames {

String ONE = “ONE”;

String TWO = “TWO”;

}

ps:其实这些都是我之前aop切换数据源的时候敲的,大概8月份的时候,这次我相当于在这个基础上着重解决事务问题

然后是将自定义注解作为切点,进行aop方式动态切换逻辑补全,创建DynamicDataSourceAspect.java:

import com.test.jtadbsource.dbConfig.DataSourceContextHolder;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.*;

import org.aspectj.lang.reflect.MethodSignature;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**

  • @Author : JCccc

  • @CreateTime : 2019/12/10

  • @Description :

**/

@Aspect

@Component

public class DynamicDataSourceAspect {

protected Logger logger = LoggerFactory.getLogger(getClass());

/**

  • 切点: 所有配置 DataSource 注解的方法

*/

@Pointcut(“@annotation(com.test.jtadbsource.dbAop.DataSource)”)

public void dataSourcePointCut() {}

@Around(“dataSourcePointCut()”)

public Object around(ProceedingJoinPoint point) throws Throwable {

DataSource ds = null;

MethodSignature signature = (MethodSignature) point.getSignature();

Method method = signature.getMethod();

//获取自定义注解

ds = method.getAnnotation(DataSource.class);

if (ds == null) {

//如果监测到自定义注解不存在,那么默认切换到数据源 mydbone

DataSourceContextHolder.setDataSourceKey(DataSourceNames.ONE);

logger.info("set default datasource is " + DataSourceNames.ONE);

} else {

//自定义存在,则按照注解的值去切换数据源

DataSourceContextHolder.setDataSourceKey(ds.value());

logger.info("set datasource is " + ds.value());

}

return point.proceed();

}

@After(value = “dataSourcePointCut()”)

public void afterSwitchDS(JoinPoint point) {

DataSourceContextHolder.clearDataSourceKey();

logger.info(“clean datasource”);

}

}

上面用到的DataSourceContextHolder.java:

/**

  • @Author : JCccc

  • @CreateTime : 2019/12/10

  • @Description :

**/

public class DataSourceContextHolder {

private static final ThreadLocal contextHolder = new ThreadLocal<>();

// 设置数据源名

public static void setDataSourceKey(String dbName) {

contextHolder.set(dbName);

}

// 获取数据源名

public static String getDataSourceKey() {

return (contextHolder.get());

}

// 清除数据源名

public static void clearDataSourceKey() {

contextHolder.remove();

}

}

ok,到这里,基本的动态切换边框的东西都完毕了,接下来是比较核心的:

1. DataSourceFactory.java  :

用于 不同的数据源DataSource的信息配置,使用DruidXADataSource创建,支持jta事务;

将不同数据源DataSource分别都关联上对应的AtomikosDataSourceBean,这样事务能提取到JTA事务管理器;

重写数据源会话工厂,为每个数据源单独配置一个。

配置重写的sqlSessionTemplate,将实际使用的不同数据源的sqlsession和spring的事务机制关联起来。

import com.alibaba.druid.pool.xa.DruidXADataSource;

import com.test.jtadbsource.dbAop.DataSourceNames;

import org.apache.ibatis.logging.stdout.StdOutImpl;

import org.apache.ibatis.session.SqlSessionFactory;

import org.mybatis.spring.SqlSessionFactoryBean;

import org.mybatis.spring.annotation.MapperScan;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import org.springframework.core.io.support.ResourcePatternResolver;

import javax.sql.DataSource;

import java.util.HashMap;

import java.util.Map;

/**

  • @Author : JCccc

  • @CreateTime : 2019/12/10

  • @Description :多数据源配置

**/

@Configuration

@MapperScan(basePackages = DataSourceFactory.BASE_PACKAGES, sqlSessionTemplateRef = “sqlSessionTemplate”)

public class DataSourceFactory {

static final String BASE_PACKAGES = “com.test.jtadbsource.mapper”;

private static final String MAPPER_LOCATION = “classpath:mybatis/mapper/*.xml”;

/***

  • 创建 DruidXADataSource mydbone 用@ConfigurationProperties 自动配置属性

*/

@Bean

@ConfigurationProperties(“spring.datasource.druid.mydbone”)

public DataSource druidDataSourceOne() {

return new DruidXADataSource();

}

/***

  • 创建 DruidXADataSource mydbtwo

*/

@Bean

@ConfigurationProperties(“spring.datasource.druid.mydbtwo”)

public DataSource druidDataSourceTwo() {

return new DruidXADataSource();

}

/**

  • 创建支持 XA 事务的 Atomikos 数据源 mydbone

*/

@Bean

public DataSource dataSourceOne(DataSource druidDataSourceOne) {

AtomikosDataSourceBean sourceBean = new AtomikosDataSourceBean();

sourceBean.setXaDataSource((DruidXADataSource) druidDataSourceOne);

// 必须为数据源指定唯一标识

sourceBean.setPoolSize(5);

sourceBean.setTestQuery(“SELECT 1”);

sourceBean.setUniqueResourceName(“mydbone”);

return sourceBean;

}

/**

  • 创建支持 XA 事务的 Atomikos 数据源 mydbtwo

*/

@Bean

public DataSource dataSourceTwo(DataSource druidDataSourceTwo) {

AtomikosDataSourceBean sourceBean = new AtomikosDataSourceBean();

sourceBean.setXaDataSource((DruidXADataSource) druidDataSourceTwo);

sourceBean.setPoolSize(5);

sourceBean.setTestQuery(“SELECT 1”);

sourceBean.setUniqueResourceName(“mydbtwo”);

return sourceBean;

}

/**

  • @param dataSourceOne 数据源 mydbone

  • @return 数据源 mydbone 的会话工厂

*/

@Bean

public SqlSessionFactory sqlSessionFactoryOne(DataSource dataSourceOne)

throws Exception {

return createSqlSessionFactory(dataSourceOne);

}

/**

  • @param dataSourceTwo 数据源 mydbtwo

  • @return 数据源 mydbtwo 的会话工厂

*/

@Bean

public SqlSessionFactory sqlSessionFactoryTwo(DataSource dataSourceTwo)

throws Exception {

return createSqlSessionFactory(dataSourceTwo);

}

/***

  • sqlSessionTemplate 与 Spring 事务管理一起使用,以确保使用的实际 SqlSession 是与当前 Spring 事务关联的,

  • 此外它还管理会话生命周期,包括根据 Spring 事务配置根据需要关闭,提交或回滚会话

  • @param sqlSessionFactoryOne 数据源 mydbone

  • @param sqlSessionFactoryTwo 数据源 mydbtwo

*/

@Bean

public CustomSqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactoryOne, SqlSessionFactory sqlSessionFactoryTwo) {

Map<Object, SqlSessionFactory> sqlSessionFactoryMap = new HashMap<>();

sqlSessionFactoryMap.put(DataSourceNames.ONE, sqlSessionFactoryOne);

sqlSessionFactoryMap.put(DataSourceNames.TWO, sqlSessionFactoryTwo);

CustomSqlSessionTemplate customSqlSessionTemplate = new CustomSqlSessionTemplate(sqlSessionFactoryOne);

customSqlSessionTemplate.setTargetSqlSessionFactories(sqlSessionFactoryMap);

return customSqlSessionTemplate;

}

/***

  • 自定义会话工厂

  • @param dataSource 数据源

  • @return :自定义的会话工厂

*/

private SqlSessionFactory createSqlSessionFactory(DataSource dataSource) throws Exception {

SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();

factoryBean.setDataSource(dataSource);

org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();

//配置驼峰命名

configuration.setMapUnderscoreToCamelCase(true);

//配置sql日志

configuration.setLogImpl(StdOutImpl.class);

factoryBean.setConfiguration(configuration);

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

//配置读取mapper.xml路径

factoryBean.setMapperLocations(resolver.getResources(MAPPER_LOCATION));

return factoryBean.getObject();

}

}

上面用到的自定义CustomSqlSessionTemplate (重写SqlSessionTemplate):

import static java.lang.reflect.Proxy.newProxyInstance;

import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;

import static org.mybatis.spring.SqlSessionUtils.closeSqlSession;

import static org.mybatis.spring.SqlSessionUtils.getSqlSession;

import static org.mybatis.spring.SqlSessionUtils.isSqlSessionTransactional;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.sql.Connection;

import java.util.List;

import java.util.Map;

import org.apache.ibatis.exceptions.PersistenceException;

import org.apache.ibatis.executor.BatchResult;

import org.apache.ibatis.session.Configuration;

import org.apache.ibatis.session.ExecutorType;

import org.apache.ibatis.session.ResultHandler;

import org.apache.ibatis.session.RowBounds;

import org.apache.ibatis.session.SqlSession;

import org.apache.ibatis.session.SqlSessionFactory;

import org.mybatis.spring.MyBatisExceptionTranslator;

import org.mybatis.spring.SqlSessionTemplate;

import org.springframework.dao.support.PersistenceExceptionTranslator;

import org.springframework.util.Assert;

public class CustomSqlSessionTemplate extends SqlSessionTemplate {

private final SqlSessionFactory sqlSessionFactory;

private final ExecutorType executorType;

private final SqlSession sqlSessionProxy;

private final PersistenceExceptionTranslator exceptionTranslator;

private Map<Object, SqlSessionFactory> targetSqlSessionFactories;

private SqlSessionFactory defaultTargetSqlSessionFactory;

/**

  • 通过Map传入

  • @param targetSqlSessionFactories

*/

public void setTargetSqlSessionFactories(Map<Object, SqlSessionFactory> targetSqlSessionFactories) {

this.targetSqlSessionFactories = targetSqlSessionFactories;

}

public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) {

this.defaultTargetSqlSessionFactory = defaultTargetSqlSessionFactory;

}

public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {

this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());

}

public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {

this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration()

.getEnvironment().getDataSource(), true));

}

public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,

PersistenceExceptionTranslator exceptionTranslator) {

super(sqlSessionFactory, executorType, exceptionTranslator);

this.sqlSessionFactory = sqlSessionFactory;

this.executorType = executorType;

this.exceptionTranslator = exceptionTranslator;

this.sqlSessionProxy = (SqlSession) newProxyInstance(

SqlSessionFactory.class.getClassLoader(),

new Class[] { SqlSession.class },

new SqlSessionInterceptor());

this.defaultTargetSqlSessionFactory = sqlSessionFactory;

}

//通过DataSourceContextHolder获取当前的会话工厂

@Override

public SqlSessionFactory getSqlSessionFactory() {

String dataSourceKey = DataSourceContextHolder.getDataSourceKey();

SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactories.get(dataSourceKey);

if (targetSqlSessionFactory != null) {

return targetSqlSessionFactory;

} else if (defaultTargetSqlSessionFactory != null) {

return defaultTargetSqlSessionFactory;

} else {

Assert.notNull(targetSqlSessionFactories, “Property ‘targetSqlSessionFactories’ or ‘defaultTargetSqlSessionFactory’ are required”);

Assert.notNull(defaultTargetSqlSessionFactory, “Property ‘defaultTargetSqlSessionFactory’ or ‘targetSqlSessionFactories’ are required”);

}

return this.sqlSessionFactory;

}

@Override

public Configuration getConfiguration() {

return this.getSqlSessionFactory().getConfiguration();

}

public ExecutorType getExecutorType() {

return this.executorType;

}

public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {

return this.exceptionTranslator;

}

/**

  • {@inheritDoc}

*/

public T selectOne(String statement) {

return this.sqlSessionProxy. selectOne(statement);

}

/**

  • {@inheritDoc}

*/

public T selectOne(String statement, Object parameter) {

return this.sqlSessionProxy. selectOne(statement, parameter);

}

/**

  • {@inheritDoc}

*/

public <K, V> Map<K, V> selectMap(String statement, String mapKey) {

return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey);

}

/**

  • {@inheritDoc}

*/

public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {

return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey);

}

/**

  • {@inheritDoc}

*/

public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {

return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey, rowBounds);

}

/**

  • {@inheritDoc}

*/

public List selectList(String statement) {

return this.sqlSessionProxy. selectList(statement);

}

/**

  • {@inheritDoc}

*/

public List selectList(String statement, Object parameter) {

return this.sqlSessionProxy. selectList(statement, parameter);

}

/**

  • {@inheritDoc}

*/

public List selectList(String statement, Object parameter, RowBounds rowBounds) {

return this.sqlSessionProxy. selectList(statement, parameter, rowBounds);

}

/**

  • {@inheritDoc}

*/

public void select(String statement, ResultHandler handler) {

this.sqlSessionProxy.select(statement, handler);

}

/**

  • {@inheritDoc}

*/

public void select(String statement, Object parameter, ResultHandler handler) {

this.sqlSessionProxy.select(statement, parameter, handler);

}

/**

  • {@inheritDoc}

*/

public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {

this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);

}

/**

  • {@inheritDoc}

*/

public int insert(String statement) {

return this.sqlSessionProxy.insert(statement);

}

/**

  • {@inheritDoc}

*/

public int insert(String statement, Object parameter) {

return this.sqlSessionProxy.insert(statement, parameter);

}

/**

  • {@inheritDoc}

*/

public int update(String statement) {

return this.sqlSessionProxy.update(statement);

}

/**

  • {@inheritDoc}

*/

public int update(String statement, Object parameter) {

return this.sqlSessionProxy.update(statement, parameter);

}

/**

  • {@inheritDoc}

*/

public int delete(String statement) {

return this.sqlSessionProxy.delete(statement);

}

/**

  • {@inheritDoc}

*/

public int delete(String statement, Object parameter) {

return this.sqlSessionProxy.delete(statement, parameter);

}

/**

  • {@inheritDoc}

*/

public T getMapper(Class type) {

return getConfiguration().getMapper(type, this);

}

/**

  • {@inheritDoc}

*/

public void commit() {

throw new UnsupportedOperationException(“Manual commit is not allowed over a Spring managed SqlSession”);

}

/**

  • {@inheritDoc}

*/

public void commit(boolean force) {

throw new UnsupportedOperationException(“Manual commit is not allowed over a Spring managed SqlSession”);

}

/**

  • {@inheritDoc}

*/

public void rollback() {

throw new UnsupportedOperationException(“Manual rollback is not allowed over a Spring managed SqlSession”);

}

/**

  • {@inheritDoc}

*/

public void rollback(boolean force) {

throw new UnsupportedOperationException(“Manual rollback is not allowed over a Spring managed SqlSession”);

}

/**

  • {@inheritDoc}

*/

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

终极手撕架构师的学习笔记:分布式+微服务+开源框架+性能优化

image

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

}

/**

  • {@inheritDoc}

*/

public void rollback() {

throw new UnsupportedOperationException(“Manual rollback is not allowed over a Spring managed SqlSession”);

}

/**

  • {@inheritDoc}

*/

public void rollback(boolean force) {

throw new UnsupportedOperationException(“Manual rollback is not allowed over a Spring managed SqlSession”);

}

/**

  • {@inheritDoc}

*/

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-qIMvP8rf-1713400763152)]

[外链图片转存中…(img-8CCWbxBI-1713400763153)]

[外链图片转存中…(img-f8ers0pt-1713400763153)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

终极手撕架构师的学习笔记:分布式+微服务+开源框架+性能优化

[外链图片转存中…(img-qqAeBm4U-1713400763153)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/2401_84010362/article/details/137902683

智能推荐

分布式光纤传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告_预计2026年中国分布式传感器市场规模有多大-程序员宅基地

文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大

07_08 常用组合逻辑电路结构——为IC设计的延时估计铺垫_基4布斯算法代码-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码

OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版

关于美国计算机奥赛USACO,你想知道的都在这_usaco可以多次提交吗-程序员宅基地

文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗

MySQL存储过程和自定义函数_mysql自定义函数和存储过程-程序员宅基地

文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程

半导体基础知识与PN结_本征半导体电流为0-程序员宅基地

文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0

随便推点

【Unity3d Shader】水面和岩浆效果_unity 岩浆shader-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader

广义线性模型——Logistic回归模型(1)_广义线性回归模型-程序员宅基地

文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型

HTML+CSS大作业 环境网页设计与实现(垃圾分类) web前端开发技术 web课程设计 网页规划与设计_垃圾分类网页设计目标怎么写-程序员宅基地

文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁_.net dll 全局目录-程序员宅基地

文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录

BRIEF特征点描述算法_breif description calculation 特征点-程序员宅基地

文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点

房屋租赁管理系统的设计和实现,SpringBoot计算机毕业设计论文_基于spring boot的房屋租赁系统论文-程序员宅基地

文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文