• [新手课堂] Spring 学习笔记
    Spring介绍Spring 是一个开源框架,是一个分层的 JavaEE 一站式框架。所谓一站式框架是指 Spring 有 JavaEE 开发的每一层解决方案。WEB层:SpringMVCService层:Spring的Bean管理,声明式事务DAO层:Spring的JDBC模板,ORM模板优点:IOC:方便解耦合AOP:对程序进行扩展轻量级框架方便与其他框架整合Spring使用Spring开发包解压后的目录介绍:docs: Spring 开发规范和APIlibs: Spring jar 包和源代码schema: Spring 配置文件的约束 DataAccess 用于数据访问,WEB 用于页面显示,核心容器也就是IOC部分。 控制反转(IOC)控制反转(Inversion of Control)是指将对象的创建权反转(交给)Spring。使用IOC就需要导入IOC相关的包,也就是上图中核心容器中的几个包:beans,context,core,expression四个包。 实现原理传统方式创建对象:UserDAO userDAO=new UserDAO();进一步面向接口编程,可以多态:UserDAO userDAO=new UserDAOImpl();这种方式的缺点是接口和实现类高耦合,切换底层实现类时,需要修改源代码。程序设计应该满足OCP元祖,在尽量不修改程序源代码的基础上对程序进行扩展。此时,可以使用工厂模式:class BeanFactory{    public static UserDAO getUserDAO(){        return new UserDAOImpl();    }}此种方式虽然在接口和实现类之间没有耦合,但是接口和工厂之间存在耦合。使用工厂+反射+配置文件的方式,实现解耦,这也是 Spring 框架 IOC 的底层实现。//xml配置文件//<bean id="userDAO" class="xxx.UserDAOImpl"></bean>class BeanFactory{    public static Object getBean(String id){        //解析XML        //反射        Class clazz=Class.forName();        return clazz.newInstance();    }}IOC XML 开发在 docs 文件中包含了 xsd-configuration.hmtl 文件。其中定义了 beans schema。<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    <bean id="userService" class="x.y.UserServiceImpl">    </bean></beans>调用类:ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService=(UserService)applicationContext.getBean("userService");userService.save();IOC 和 DIDI 指依赖注入,其前提是必须有 IOC 的环境,Spring 管理这个类的时候将类的依赖的属性注入进来。例如,在UserServiceImpl.java中:public class UserServiceImpl implements UserService{private String name;public void setName(String name){this.name=name;}public void save(){System.out.println("save "+name);}}在配置文件中:<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="userService" class="spring.demo1.UserServiceImpl">    <!--配置依赖的属性-->     <property name="name" value="tony"/>    </bean></beans>测试代码:@Testpublic void demo2(){//创建Spring工厂ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService=(UserService)applicationContext.getBean("userService");userService.save();}运行结果:save tony可以看到,在配置文件中配置的属性,在 Spring 管理该类的时候将其依赖的属性成功进行了设置。如果不使用依赖注入,则无法使用接口,只能使用实现类来进行设置,因为接口中没有该属性。Spring 的工厂类BeanFactory: 老版本的工厂类,在调用getBean()方法时,才会生成类的实例。ApplicationContext: 在加载配置文件的时候,就会将 Spring 管理的类都实例化。有两个实现类:ClassPathXmlApplicationContext: 加载类路径下的配置文件FileSystemXmlApplicationContext: 加载磁盘下的配置文件bean标签配置id: 唯一约束,不能出现特殊字符name: 理论上可以重复,但是开发中最好不要。可以出现特殊字符生命周期:init-method: bean被初始化的时候执行的方法destroy-method: bean被销毁的时候执行的方法作用范围:scope: bean的作用范围,有如下几种,常用的是前两种singleton: 默认使用单例模式创建prototype: 多例request: 在web项目中,spring 创建类后,将其存入到 request 范围中session: 在web项目中,spring 创建类后,将其存入到 session 范围中globalsession: 在web项目中,必须用在 porlet 环境属性注入设置构造方法方式的属性注入: Car 类在构造方法中有两个属性,分别为 name 和 price。<bean id="car" class="demo.Car">    <constructor-arg name="name" value="bmw">    <constructor-arg name="price" value="123"></bean>set 方法属性注入: Employee 类在有两个 set 方法,分别设置普通类型的 name 和引用类型的 Car (使用 ref 指向引用类型的 id 或  name)。<bean id="employee" class="demo.Employee">    <property name="name" value="xiaoming">    <property name="car" ref="car"></bean>P名称空间的属性注入: 首先需要引入p名称空间:<beans xmlns="http://www.springframework.org/schema/beans"    //引入p名称空间    xmlns:p="http://www.springframework.org/schema/p"    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"></beans>如果是普通属性:<bean id="car" class="demo.Car" p:name="bmv" p:price="123"></bean>如果是引用类型:<bean id="employee" class="demo.Employee" p:name="xiaoming" p:car-ref:"car"></bean>SpEL(Spring Expression Language)属性注入(Spring 3.x以上版本)<bean id="car" class="demo.Car">    <property name="name" value="#{'xiaoming'}">    <property name="car" ref="#{car}"></bean>集合类型属性注入:<bean id="car" class="demo.Car">    <property name="namelist">        <list>            <value>qirui</value>            <value>baoma</value>            <value>benchi</value>        </list>    </property></bean>多模块开发配置在加载配置文件的时候,加载多个配置文件在一个配置文件中引入多个配置文件,通过实现IOC 注解开发示例引入jar包: 除了要引入上述的四个包之外,还需要引入aop包。创建 applicationContext.xml ,使用注解开发引入 context 约束(xsd-configuration.html)<beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">         <!-- bean definitions here --></beans>组件扫描: 使用IOC注解开发,需要配置组件扫描,也就是哪些包下的类使用IOC的注解。<context:component-scan base-package="demo1">在类上添加注解使用注解设置属性的值属性如果有set方法,将属性注入的注解添加到set方法属性没有set方法,将注解添加到属性上。@Component("UserDao")//相当于配置了一个<bean> 其id为UserDao,对应的类为该类public class UserDAOImpl implements UserDAO {@Overridepublic void save() {// TODO Auto-generated method stubSystem.out.println("save");} }注解详解@Component组件注解,用于修饰一个类,将这个类交给 Spring 管理。有三个衍生的注解,功能类似,也用来修饰类。@Controller:修饰 web 层类@Service:修饰 service 层类@Repository:修饰 dao 层类2.属性注入普通属性使用 @Value 来设置属性的值对象属性使用 @Autowired  ,这个注解是按照类型来进行属性注入的。如果希望按照 bean 的名称或id进行属性注入,需要用 @Autowired 和 @Qualifier 一起使用实际开发中,使用 @Resource(name=" ") 来进行按照对象的名称完成属性注入3.其他注解@PostConstruct 相当于 init-method,用于初始化函数的注解@PreDestroy 相当于 destroy-method,用于销毁函数的注解@Scope 作用范围的注解,常用的是默认单例,还有多例 @Scope("prototype")IOC 的 XML 和注解开发比较适用场景:XML 适用于任何场景;注解只适合自己写的类,不是自己提供的类无法添加注解。可以使用 XML 管理 bean,使用注解来进行属性注入AOP开发AOP 是 Aspect Oriented Programming 的缩写,意为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,是OOP的延续。AOP 能够对程序进行增强,在不修改源码的情况下,可以进行权限校验,日志记录,性能监控,事务控制等。也就是说功能分为两大类,一类是核心业务功能,一类是辅助增强功能。两类功能彼此独立进行开发。比如登录功能是核心业务功能,日志功能是辅助增强功能,如果有需要,将日志和登录编制在一起。辅助功能就称为切面,这种能选择性的、低耦合的把切面和核心业务功能结合的编程思想称为切面编程。底层实现JDK 动态代理只能对实现了接口的类产生代理。Cglib 动态代理可以对没有实现接口的类产生代理对象,生成的是子类对象。使用 JDK 动态代理:public interface UserDao {public void insert();public void delete();public void update();public void query();}实现类:public class UserDaoImpl implements UserDao { @Override public void insert() { System.out.println("insert"); } @Override public void delete() { System.out.println("delete"); } @Override public void update() { System.out.println("update"); } @Override public void query() { System.out.println("query"); } }JDK 代理:public class JDKProxy implements InvocationHandler{private UserDao userDao;public JDKProxy(UserDao userDao){this.userDao=userDao;}public UserDao createProxy(){UserDao userDaoProxy=(UserDao)Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(), this);return userDaoProxy;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if("update".equals(method.getName())){System.out.println("权限校验");return method.invoke(userDao, args);}return method.invoke(userDao, args);}}通过动态代理增强了 update 函数。 测试类:public class Demo1 {@Testpublic void demo1(){UserDao userDao=new UserDaoImpl();UserDao proxy=new JDKProxy(userDao).createProxy();proxy.insert();proxy.delete();proxy.update();proxy.query();}}运行结果为:insertdelete权限校验updatequeryCglibCglib 是第三方开源代码生成类库,可以动态添加类的属性和方法。与上边JDK代理不同,Cglib的使用方式如下:public class CglibProxy implements MethodInterceptor{//传入增强的对象private UserDao customerDao;public CglibProxy(UserDao userDao){this.userDao=userDao;}public UserDao createProxy(){Enhancer enhancer=new Enhancer();enhancer.setSuperclass(userDao.getClass());enhancer.setCallback(this);UserDao proxy=(UserDao)enhancer.create();return proxy;}@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {if("save".equals(method.getName())){System.out.println("enhance function");return methodProxy.invokeSuper(proxy, args);}return methodProxy.invokeSuper(proxy, args);}}如果实现了接口的类,底层采用JDK代理。如果不是实现了接口的类,底层采用 Cglib代理。IOC与传统方式的比较获取对象方式:传统通过 new 关键字主动创建一个对象。IOC 方式中,将对象的生命周期交给 Spring 管理,直接从 Spring 获取对象。也就是控制反转————将控制权从自己手中交到了 Spring 手中。Spring 的 AOP 开发(AspectJ 的 XML 方式)AspectJ 是一个 AOP 的框架,Spring 引入 AspectJ,基于 AspectJ 进行 AOP 的开发。相关术语Joinpoint: 连接点,可以被拦截到的点。也就是可以被增强的方法都是连接点。Pointcut: 切入点,真正被拦截到的点,也就是真正被增强的方法Advice: 通知,方法层面的增强。对某个方法进行增强的方法,比如对 save 方法进行权限校验,权限校验的方法称为通知。Introduction: 引介,类层面的增强。Target: 目标,被增强的对象(类)。Weaving: 织入,将 advice 应用到 target 的过程。Proxy: 代理对象,被增强的对象。Aspect: 切面,多个通知和多个切入点的组合。使用方法引入相关包引入配置文件<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --></beans>编写目标类并配置:public class ProductDaoImpl implements ProductDao {@Overridepublic void save() {System.out.println("save");}@Overridepublic void update() {System.out.println("update");}@Overridepublic void find() {System.out.println("find");}@Overridepublic void delete() {System.out.println("delete");}}<bean id="productDao" class="demo1.ProductDaoImpl"></bean>编写切面类,假设用于权限验证并配置public class MyAspectXML {public void checkPri(){System.out.println("check auth");}}<bean id="myAspect" class="demo1.MyAspectXML"></bean>通过AOP配置完成对目标类的增强<aop:config><aop:pointcut expression="execution(* demo1.ProductDaoImpl.save(..))" id="pointcut1"/><aop:aspect ref="myAspect"><aop:before method="chechPri" pointcut-ref="pointcut1"/></aop:aspect> </aop:config> 通知类型前置通知:在目标方法执行前操作,可以获得切入点信息<aop:before method="chechPri" pointcut-ref="pointcut1"/>public void checkPri(JoinPoint joinPoint){System.out.println("check auth "+joinPoint);}后置通知:在目标方法执行后操作,可以获得方法返回值<aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>public void writeLog(Object result){    System.out.println("writeLog "+result);}环绕通知:在目标方法执行前和后操作,可以阻止目标方法执<aop:around method="around" pointcut-ref="pointcut3"/>public Object around(ProceedingJoinPoint joinPoint) throws Throwable{System.out.println("before");Object result=joinPoint.proceed();System.out.println("after");return result;}异常抛出通知:程序出现异常时操作<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>public void afterThrowing(Throwable ex){System.out.println("exception "+ex.getMessage());}最终通知:相当于finally块,无论代码是否有异常,都会执行<aop:after method="finallyFunc" pointcut-ref="pointcut4"/>public void finallyFunc(){System.out.println("finally");}引介通知:不常用Spring 切入点表达式基于 execution 函数完成语法:[访问修饰符] 方法返回值 包名.类名.方法名(参数)其中任意字段可以使用*代替表示任意值Spring 的 AOP 基于 AspectJ 注解开发开发步骤引入jar包设置配置文件:<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"></beans>编写配置目标类<bean id="orderDao" class="demo1.OrderDao"></bean>public class OrderDao {public void save(){System.out.println("save order");}public void update(){System.out.println("update order");}public void delete(){System.out.println("delete order");}public void find(){System.out.println("find order");}}开启aop注解自动代理<aop:aspectj-autoproxy/>编写切面类并配置@Aspectpublic class MyAspectAnno {@Before(value="execution(* demo1.OrderDao.save(..))")public void before(){System.out.println("before");}}<bean id="myAspect" class="demo1.MyAspectAnno">注解通知类型@Before: 前置通知@AfterReturning: 后置通知@AfterReturning(value="execution(* demo1.OrderDao.save(..))",returning="result")public void after(Object result){System.out.println("after "+result);}@Around:环绕通知@Around(value="execution(* demo1.OrderDao.save(..))")public Object around(ProceedingJoinPoint joinPoint) throws Throwable{System.out.println("before");Object obj=joinPoint.proceed();System.out.println("after");return obj;}@AfterThrowing: 抛出异常@AfterThrowing(value="execution(* demo1.OrderDao.save(..))",throwing="e")public void afterThrowing(Throwable e){System.out.println("exception:"+e.getMessage();}@After: 最终通知@After(value="execution(* demo1.OrderDao.save(..))")public void after(){System.out.println("finally");}@PointCut:切入点注解@PointCut(value="execution(* demo1.OrderDao.save(..))")private void pointcut1(){}此时,在上述通知的注解中,value可以替换为该函数名,例如:@After(value="MyAspect.pointcut1()")public void after(){System.out.println("finally");}这个注解的好处是,只需要维护切入点即可,不用在修改时修改每个注解。Spring 的 JDBC 模板Spring 对持久层也提供了解决方案,也就是 ORM 模块和 JDBC 的模板。针对 JDBC ,提供了 org.springframework.jdbc.core.JdbcTemplate 作为模板类。使用 JDBC 模板引入jar包,数据库驱动,Spring 的 jdbc 相关包。基本使用:public void demo1(){    //创建连接池DriverManagerDataSource dataSource=new DriverManagerDataSource();dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql:///spring4");dataSource.setUsername("root");dataSource.setPassword("123456");//创建JDBC模板 JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource);jdbcTemplate.update("insert into account values (null,?,?)", "xiaoming",1000d);}将连接池和模板交给 Spring 管理配置文件:<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource;"><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql:///spring4"></property><property name="username" value="root"></property><property name="password" value="123456"></property></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate;"> <property name="dataSource" ref="dataSource"></property></bean>测试文件: @RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext.xml")public class JdbcDemo2 {@Resource(name="jdbcTemplate")private JdbcTemplate jdbcTemplate;@Testpublic void demo2(){jdbcTemplate.update("insert into account values (null,?,?)", "xiaolan",1000d);}}使用开源数据库连接池使用 DBCP 的配置:<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql://192.168.66.128/spring4"></property><property name="username" value="root"></property><property name="password" value="123456"></property>使用 C3P0 的配置:<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="com.mysql.jdbc.Driver"></property><property name="jdbcUrl" value="jdbc:mysql://192.168.66.128/spring4"></property><property name="user" value="root"></property><property name="password" value="123456"></property></bean>引入外部属性文件首先建立外部属性文件:jdbc.driverClass=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://192.168.66.128/spring4jdbc.username=rootjdbc.password=123456然后对属性文件进行配置:<context:property-placeholder location="classpath:jdbc.properties"/><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="${jdbc.driverClass}"></property><property name="jdbcUrl" value="${jdbc.url}"></property><property name="user" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean>CRUD操作insert, update, delete 语句都借助模板的 update 方法进行操作。public void demo(){jdbcTemplate.update("insert into account values (null,?,?)", "xiaoda",1000d);jdbcTemplate.update("update account set name=?,money=? where id=?", "xiaoda",1000d,2);jdbcTemplate.update("delete from account where id=?", 6);}查询操作:public void demo3(){String name=jdbcTemplate.queryForObject("select name from account where id=?",String.class,5);long count=jdbcTemplate.queryForObject("select count(*) from account",Long.class);}将返回的结果封装成为类:public void demo4(){Account account=jdbcTemplate.queryForObject("select * from account where id=?", new MyRowMapper(),5);}其中:class MyRowMapper implements RowMapper<Account>{@Overridepublic Account mapRow(ResultSet rs, int rowNum) throws SQLException {Account account=new Account();account.setId(rs.getInt("id"));account.setName(rs.getString("name"));account.setMoney(rs.getDouble("money"));return account;}}Spring的事务管理事务事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。具有四个特性:原子性:事务不可分一致性:事务执行前后数据完整性保持一致隔离性:一个事务的执行不应该受到其他事务干扰持久性:一旦事务结束,数据就持久化到数据库如果不考虑隔离性会引发安全性问题:读问题:脏读:一个事务读到另一个事务未提交的数据不可重复读:一个事务读到另一个事务已经提交的 update 数据,导致一个事务中多次查询结果不一致幻读:一个事务读到另一个事务已经提交的 insert 数据,导致一个事务中多次查询结果不一致写问题:丢失更新解决读问题:设置事务隔离级别Read uncommitted: 未提交读,无法解决任何读问题Read committed: 已提交读,解决脏读问题Repeatable read: 重复读,解决脏读和不可重复读问题Serializable:序列化,解决所有读问题事务管理APIPlatformTransactionManager: 平台事务管理器这是一个接口,拥有多个不同的实现类,如 DataSourceTransactionManager 底层使用了JDBC 管理事务; HibernateTransactionManager 底层使用了 Hibernate 管理事务。TransactionDefinition: 事务定义信息用于定义事务的相关信息,如隔离级别、超时信息、传播行为、是否只读等TransactionStatus: 事务的状态用于记录在事务管理过程中,事务的状态的对象。上述API的关系: Spring 在进行事务管理的时候,首先平台事务管理器根据事务定义信息进行事务管理,在事务管理过程当中,产生各种此状态,将这些状态信息记录到事务状态的对象当中。事务的传播行为事务的传播行为主要解决业务层(Service)方法相互调用的问题,也就是不同的业务中存在不同的事务时,如何操作。Spring 中提供了7种事务的传播行为,分为三类:保证多个操作在同一个事务中PROPAGATION_REQUIRED: B方法调用A方法,如果A中有事务,使用A中的事务并将B中的操作包含到该事务中;否则新建一个事务,将A和B中的操作包含进来。(默认)PROPAGATION_SUPPORTS:如果A中有事务,使用A的事务;否则不使用事务PROPAGATION_MANDATORY:如果A中有事务,使用A的事务;否则抛出异常保证多个操作不在同一个事务中PROPAGATION_REQUIRES_NEW:如果A中有事务,将其挂起,创建新事务,只包含自身操作。否则,新建一个事务,只包含自身操作。PROPAGATION_NOT_SUPPORTED:如果A中有事务,挂起,不使用事务。PROPAGATION_NEVER:如果A中有事务,抛出异常,也即不能用事务运行。嵌套事务PROPAGATION_NESTED:如果A有事务,按照A的事务执行,执行完成后,设置一个保存点,然后执行B的操作。如果出现异常,可以回滚到最初状态或保存点状态。实例以转账为例,业务层的DAO层类如下:public interface AccountDao {public void outMoney(String from,Double money);public void inMoney(String to,Double money);}public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{@Overridepublic void outMoney(String from, Double money) { this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,from);}@Overridepublic void inMoney(String to, Double money) {this.getJdbcTemplate().update("update account set money = money + ? where name = ?",money,to);}}public interface AccountService {public void transfer(String from,String to,Double money);}public class AccountServiceImpl implements AccountService {private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}@Overridepublic void transfer(String from, String to, Double money) {accountDao.outMoney(from, money);accountDao.inMoney(to, money);}}在xml中进行类的配置:<bean id="accountService" class="tx.demo.AccountServiceImpl"><property name="accountDao" ref="accountDao"/></bean><bean id="accountDao" class="tx.demo.AccountDaoImpl"><property name="dataSource" ref="dataSource"/></bean> 
  • [技术干货] 常见的JDBC组件
    JDBC API是一个Java API可以访问任何类型的表格格式数据,存储在一个关系数据库尤其是数据。JDBC API提供了以下接口和类DriverManager: 这个类管理数据库驱动程序的列表。内容是否符合从Java应用程序使用的通信子协议正确的数据库驱动程序的连接请求。识别JDBC在一定子协议的第一个驱动器将被用来建立数据库连接。Driver: 此接口处理与数据库服务器通信。很少直接与驱动程序对象。相反,使用DriverManager中的对象,它管理此类型的对象。它也抽象与驱动程序对象工作相关的详细信息Connection : 此接口与接触数据库的所有方法。连接对象表示通信上下文,即,与数据库中的所有的通信是通过唯一的连接对象。Statement : 可以使用这个接口创建的对象的SQL语句提交到数据库。一些派生的接口接受除执行存储过程的参数。ResultSet: 这些对象保存从数据库后,执行使用Statement对象的SQL查询中检索数据。它作为一个迭代器,让您可以通过移动它的数据。SQLException: 这个类处理发生在一个数据库应用程序的任何错误。
  • [对接系列] python3 通过JDBC连接dws
    1环境说明:    python 3.6+    java 1.8.0.402前置安装:    安装python第三方库jaydebeapi、jpype1。     1)安装jpype :        pip3 install jpype1   -i http://pypi.douban.com/simple --trusted-host pypi.douban.com     2)安装JayDeBeApi :         pip3 install JayDeBeApi  -i http://pypi.douban.com/simple --trusted-host pypi.douban.com      3) 安装java       yum install java3下载DWS JDBC包:   登录华为云->DWS-->连接管理:下载JDBC驱动包4 配置测试案例:vi test1.py 贴入内容import jaydebeapiurl = '通过华为云---DWS---连接管理---JDBC连接字符串(公网)'user = '账号'password = '密码'dirver = 'com.huawei.gauss200.jdbc.Driver'jarFile = '/root/jdbc/gsjdbc200.jar' #下载的DWS JDBC包sqlStr ='select 1'       #测试的sqlconn = jaydebeapi.connect(dirver,url,[user,password],jarFile)curs=conn.cursor()curs.execute(sqlStr)result=curs.fetchall()print(result)curs.close()conn.close() 执行:python3 test1.py 
  • [基础组件] 【MRS产品】【Hive元数据功能】如何使用JDBC的方式连接Hive元数据?
    【功能模块】【操作步骤&问题现象】1、用JAVA编写JDBC连接hive元数据,但是无法登录【截图信息】连接信息如下报错信息如下我尝试修改/srv/BigData/dbdata_service/data/pg_hba.conf,然后重启DBService之后,这个文件的变动又被改回去了【日志信息】(可选,上传日志内容或者附件)
  • [技术干货] 通过JDBC连接oracle数据库的十大技巧
    Java数据库连接(JDBC)API是一系列能够让Java编程人员访问数据库的接口,各个开发商的接口并不完全相同。在使用多年的Oracle公司的JDBC后,我积累了许多技巧,这些技巧能够使我们更好地发挥系统的性能和实现更多的功能。   1、在客户端软件开发中使用Thin驱动程序   在开发Java软件方面,Oracle的数据库提供了四种类型的驱动程序,二种用于应用软件、applets、servlets等客户端软件,另外二种用于数据库中的Java存储过程等服务器端软件。在客户机端软件的开发中,我们可以选择OCI驱动程序或Thin驱动程序。OCI驱动程序利用Java本地化接口(JNI),通过Oracle客户端软件与数据库进行通讯。Thin驱动程序是纯Java驱动程序,它直接与数据库进行通讯。为了获得最高的性能,Oracle建议在客户端软件的开发中使用OCI驱动程序,这似乎是正确的。但我建议使用Thin驱动程序,因为通过多次测试发现,在通常情况下,Thin驱动程序的性能都超过了OCI驱动程序。   2、关闭自动提交功能,提高系统性能   在第一次建立与数据库的连接时,在缺省情况下,连接是在自动提交模式下的。为了获得更好的性能,可以通过调用带布尔值false参数的Connection类的setAutoCommit()方法关闭自动提交功能,如下所示:  conn.setAutoCommit(false);值得注意的是,一旦关闭了自动提交功能,我们就需要通过调用Connection类的commit()和rollback()方法来人工的方式对事务进行管理。   3、在动态SQL或有时间限制的命令中使用Statement对象   在执行SQL命令时,我们有二种选择:可以使用PreparedStatement对象,也可以使用Statement对象。无论多少次地使用同一个SQL命令,PreparedStatement都只对它解析和编译一次。当使用Statement对象时,每次执行一个SQL命令时,都会对它进行解析和编译。这可能会使你认为,使用PreparedStatement对象比使用Statement对象的速度更快。然而,我进行的测试表明,在客户端软件中,情况并非如此。因此,在有时间限制的SQL操作中,除非成批地处理SQL命令,我们应当考虑使用Statement对象。   此外,使用Statement对象也使得编写动态SQL命令更加简单,因为我们可以将字符串连接在一起,建立一个有效的SQL命令。因此,我认为,Statement对象可以使动态SQL命令的创建和执行变得更加简单。   4、利用helper函数对动态SQL命令进行格式化   在创建使用Statement对象执行的动态SQL命令时,我们需要处理一些格式化方面的问题。例如,如果我们想创建一个将名字O'Reilly插入表中的SQL命令,则必须使用二个相连的“''”号替换O'Reilly中的“'”号。完成这些工作的最好的方法是创建一个完成替换操作的helper方法,然后在连接字符串心服用公式表达一个SQL命令时,使用创建的helper方法。与此类似的是,我们可以让helper方法接受一个Date型的值,然后让它输出基于Oracle的to_date()函数的字符串表达式。   5、利用PreparedStatement对象提高数据库的总体效率   在使用PreparedStatement对象执行SQL命令时,命令被数据库进行解析和编译,然后被放到命令缓冲区。然后,每当执行同一个PreparedStatement对象时,它就会被再解析一次,但不会被再次编译。在缓冲区中可以发现预编译的命令,并且可以重新使用。在有大量用户的企业级应用软件中,经常会重复执行相同的SQL命令,使用PreparedStatement对象带来的编译次数的减少能够提高数据库的总体性能。如果不是在客户端创建、预备、执行PreparedStatement任务需要的时间长于Statement任务,我会建议在除动态SQL命令之外的所有情况下使用PreparedStatement对象。  6、在成批处理重复的插入或更新操作中使用PreparedStatement对象     如果成批地处理插入和更新操作,就能够显著地减少它们所需要的时间。Oracle提供的Statement和 CallableStatement并不真正地支持批处理,只有PreparedStatement对象才真正地支持批处理。我们可以使用addBatch()和executeBatch()方法选择标准的JDBC批处理,或者通过利用PreparedStatement对象的setExecuteBatch()方法和标准的executeUpdate()方法选择速度更快的Oracle专有的方法。要使用Oracle专有的批处理机制,可以以如下所示的方式调用setExecuteBatch():
  • [问题求助] 【DWS产品】【JDBC驱动数据写入功能】SparkApp写入数据异常
    【功能模块】GaussDB(DWS)驱动在SparkApp中使用问题【操作步骤&问题现象】1、在同一个DWS库及模式下,现有两张结构相同的表,分别称为表A、表B。其中表A有大约160万行数据2、编写简单的SparkApp,依赖 gsjdbc200.jar(driverClass=com.huawei.gauss200.jdbc.Driver)和 gsjdbc4.jar(driverClass=org.postgresql.Driver)首先使用gsjdbc200.jar作为驱动读取A表数据,得到Dataset实例Dataset ds = sparkSession.read().format("jdbc") .option("url", "jdbc:gaussdb://xx.xxx.xx.xx:8000/dwhm") .option("driver", "com.huawei.gauss200.jdbc.Driver") .option("user", "******") .option("password", "**********") .option("dbtable", "表A") .option("numPartitions", 6) .option("lowerBound", 0) .option("upperBound", 1728454) .option("partitionColumn", "id_column") .option("fetchsize", 1024) .load();然后立即再使用gsjdbc200.jar(driverClass=com.huawei.gauss200.jdbc.Driver)作为驱动,尝试将A表数据写入B表ds.write().format("jdbc") .option("dbtable", "表B") .mode(SaveMode.Overwrite) .option("url", "jdbc:gaussdb://10.2.121.11:8000/dwhm") .option("driver", "com.huawei.gauss200.jdbc.Driver") .option("user", "xxxxxx") .option("password", "**************") .option("truncate", true) .option("batchsize", 500) .save();读取到数据后,在开始执行数据写入task时很快就报出如下异常而失败。java.sql.BatchUpdateException: Batch entry 0 INSERT INTO 表B名称 ("po_process_action_id","tenant_id","po_header_id","po_line_id","po_line_location_id","display_line_num","display_line_location_num","version_num","process_type_code","process_remark","processed_date","process_user_id","process_user_name","object_version_number","creation_date","created_by","last_updated_by","last_update_date","storage_time") VALUES ('1155064','20453','194759',NULL,NULL,NULL,NULL,'1','UPDATE',NULL,'2021-07-11 01:14:21+08','859953','华为接口账号','1','2021-07-11 01:14:21+08','859953','859953','2021-07-11 01:14:21+08','2021-09-27 13:47:06+08') was aborted: ERROR: invalid input syntax for type oid: "" Call getNextException to see other errors in the batch. at com.huawei.gauss200.jdbc.jdbc.BatchResultHandler.handleError(BatchResultHandler.java:152) at com.huawei.gauss200.jdbc.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2584) at com.huawei.gauss200.jdbc.core.v3.QueryExecutorImpl.executeBatch(QueryExecutorImpl.java:575) at com.huawei.gauss200.jdbc.jdbc.PgStatement.executeBatch(PgStatement.java:879) at com.huawei.gauss200.jdbc.jdbc.PgPreparedStatement.executeBatch(PgPreparedStatement.java:1580) at org.apache.spark.sql.execution.datasources.jdbc.JdbcUtils$.savePartition(JdbcUtils.scala:654) at org.apache.spark.sql.execution.datasources.jdbc.JdbcUtils$$anonfun$saveTable$1.apply(JdbcUtils.scala:821) at org.apache.spark.sql.execution.datasources.jdbc.JdbcUtils$$anonfun$saveTable$1.apply(JdbcUtils.scala:821) at org.apache.spark.rdd.RDD$$anonfun$foreachPartition$1$$anonfun$apply$29.apply(RDD.scala:935) at org.apache.spark.rdd.RDD$$anonfun$foreachPartition$1$$anonfun$apply$29.apply(RDD.scala:935) at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:2074) at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:2074) at org.apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:87) at org.apache.spark.scheduler.Task.run(Task.scala:109) at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:345) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) Caused by: com.huawei.gauss200.jdbc.util.PSQLException: ERROR: invalid input syntax for type oid: "" at com.huawei.gauss200.jdbc.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2852) at com.huawei.gauss200.jdbc.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2583) ... 16 more表B只是普通的行存表,其中 po_process_action_id 为主键。表内没有名为“oid”的用户定义列。如果换用 gsjdbc4.jar(driverClass=org.postgresql.Driver)作为 write 时的驱动,只相应改动 url、driver 两项option,其它代码不做任何改变,则数据写入可以成功,没有任何问题。使用Spark版本为 2.3.2,程序运行于本地笔记本,原意为简单试一下Spark读写DWS
  • [问题求助] 【MRS】通过jdbc读取hbase映射的hive表报错
    适配hbase过程中,通过jdbc读取hbase映射的hive表报错报错信息在附件异常信息.txt中,网上查阅资料说需要修改集群的配置但是尝试后并无效果,阻塞了hbase的适配。
  • [问题求助] 【MRS】【JDBC方式访问Spark SQL】JDBC方式访问Spark SQL样例程序本地编译器调测报错
    【功能模块】MRS 8.0.2混合云版本   Spark 组件  Spark SQL,通过JDBC方式访问Spark SQL【操作步骤&问题现象】1、调测样例   “SparkThriftServerScalaExample”,修改对应的参数信息,在安装MRS客户端的虚拟机上执行如下命令是可以运行成功的java -cp ${SPARK_HOME}/jars/*:${SPARK_HOME}/conf:/home/openlab/SparkThriftServerExample-1.0.jar com.huawei.bigdata.spark.examples.ThriftServerQueriesTest ${SPARK_HOME}/conf/hive-site.xml ${SPARK_HOME}/conf/spark-defaults.conf2、在本地开发的编译器中,修改对应的参数信息,进行运行程序报如下错误Exception in thread "main" java.lang.IllegalArgumentException: Illegal character in path at index 212: hive2://dummyhost:00000/;serviceDiscoveryMode=zooKeeper;zooKeeperNamespace=sparkthriftserver2x;saslQop=auth-conf;auth=KERBEROS;principal=spark2x/hadoop.HADOOP.COM@HADOOP.COM;user.principal=wx657505;user.keytab=D:\conf\user.keytab;    at java.net.URI.create(URI.java:852)    at org.apache.hive.jdbc.Utils.parseURL(Utils.j【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [其他] 【JDBC】JDBC使用过程中的常见报错及处理方法
    1. JDBC报错:Invalid username/password, login denied这类问题首先确认用户名和密码正确,确认正确之后请确认所用JDBC驱动版本是否是GaussDB配套的驱动版本,建议更换gauss200的驱动包重新验证。驱动连接串配置参考:https://bbs.huaweicloud.com/forum/thread-59902-1-1.html2. 出现报错:prepared statement '%s' already exists确认没有重复使用prepare语句之后,可尝试设置prepareThreshold=03. 出现报错:portal "%s" does not exist使用setFetchSize分批获取结果集时,需关闭autocommit,如果手动commit后继续获取结果集,会出现该报错,需排查本次获取结果集后,下次获取结果集前,是否有手动commit的操作
  • [问题求助] GaussDB A 8.0.0.5 数据库通过jdbc发送给客户端的数据性能是否有参数控制
    数据库通过jdbc发送给客户端的数据性能有没有参数控制?比如我查询163万条数据需要2s,全部发送给客户端需要25s,这个性能有没有参数控制?
  • [生态空间] GaussDB A 8.0.0.5 数据库通过jdbc发送给客户端的数据性能是否有参数控制
    数据库通过jdbc发送给客户端的数据性能有没有参数控制?比如我查询163万条数据需要2s,全部发送给客户端需要25s,这个性能有没有参数控制?
  • [数据编排] 数据编排-数据流-JDBC加载节点填了模式和物理模型,但是参数配置页签中没有带出属性,怀疑数据源没有连通,但是数据源管理界面没有
    配置了数据源和模式及表名未带出字段属性:
  • [已解决问题归档] 【AICC CC-CMS产品】【报表功能】jdbc连接oracle数据库返回结果中文乱码
    【问题来源】【必填】    【武汉农商行】    【问题简要】【必填】     AICC CC-CMS产品中通过jdbc连接华为测试库,返回结果中文乱码。【问题类别】【必填】    【aicc cc-cms】【AICC解决方案版本】【必填】    【AICC可选择版本:AICC 8.13.0 CC-CMS】【期望解决时间】【选填】      尽快【问题现象描述】【必填】         //描述做了什么,期望发生什么,实际发生了什么,发生问题坐席等。如果已经尝试了一些措施但是没有解决问题,也请描述出来。         //请描述环境信息,如:华东,华北等。         1.AICC产品升级后,通过jdbc连接华为数据库,查询结果显示乱码。
  • [技术干货] MySQL JDBC中的参数
    #### jdbc的参数配置 当我们用jdbc连MySQL的时候,有一个连接串,一般形如 ``` jdbc:mysql://127.0.0.1:3307/test_tb?connectTimeout=5000&serverTimezone=UTC&zeroDateTimeBehavior=convertToNull&characterEncoding=UTF8&useConfigs=fullDebug ``` 还有时候我们会用一个properties对象配置参数,这些参数是怎么发挥作用的,而且有什么约束呢,带着这个问题,我们慢慢研究一下jdbc的源码 ##### 参数传递 1、jdbc中连接数据库的入口在DriverManager的getConnection中,会把user,password放入properties中 ``` //DriverManager.java @CallerSensitive public static Connection getConnection(String url, java.util.Properties info) throws SQLException { return (getConnection(url, info, Reflection.getCallerClass())); } @CallerSensitive public static Connection getConnection(String url, String user, String password) throws SQLException { java.util.Properties info = new java.util.Properties(); if (user != null) { info.put("user", user); } if (password != null) { info.put("password", password); } return (getConnection(url, info, Reflection.getCallerClass())); } @CallerSensitive public static Connection getConnection(String url) throws SQLException { java.util.Properties info = new java.util.Properties(); return (getConnection(url, info, Reflection.getCallerClass())); } ``` 2、MySQL jdbc建立连接的处理在NonRegisteringDriver的connect中,会把连接串拼接成url§{properties}格式,生成ConnectionUrl对象包装起来 ``` //com.mysql.cj.conf.ConnectionUrl#buildConnectionStringCacheKey private static String buildConnectionStringCacheKey(String connString, Properties info) { StringBuilder sbKey = new StringBuilder(connString); sbKey.append("\u00A7"); // Section sign. sbKey.append( info == null ? null : info.stringPropertyNames().stream().map(k -> k + "=" + info.getProperty(k)).collect(Collectors.joining(", ", "{", "}"))); return sbKey.toString(); } ``` 3、然后整个url§{properties}会给ConnectionUrlParser处理,通过CONNECTION_STRING_PTRN正则匹配出scheme(jdbc:mysql), authority(ip:port), path(dbname),query(url中的参数部分),并通过PROPERTIES_PTRN匹配出url中的参数对 ``` Pattern CONNECTION_STRING_PTRN = Pattern.compile("(?[\\w\\+:%]+)\\s*" // scheme: required; alphanumeric, plus, colon or percent + "(?://(?[^/?#]*))?\\s*" // authority: optional; starts with "//" followed by any char except "/", "?" and "#" + "(?:/(?!\\s*/)(?[^?#]*))?" // path: optional; starts with "/" but not followed by "/", and then followed by by any char except "?" and "#" + "(?:\\?(?!\\s*\\?)(?[^#]*))?" // query: optional; starts with "?" but not followed by "?", and then followed by by any char except "#" + "(?:\\s*#(?.*))?"); Pattern PROPERTIES_PTRN = Pattern.compile("[&\\s]*(?[\\w\\.\\-\\s%]*)(?:=(?[^&]*))?"); ``` 4、最后通过ConnectionUrl的collectProperties,把参数放入ConnectionUrl自己的properties里面去。由此可见,通过url配置参数与properties配置参数效果基本是一样的 ``` //com.mysql.cj.conf.ConnectionUrl#collectProperties protected void collectProperties(ConnectionUrlParser connStrParser, Properties info) { // Fill in the properties from the connection string. connStrParser.getProperties().entrySet().stream().forEach(e -> this.properties.put(PropertyKey.normalizeCase(e.getKey()), e.getValue())); // Properties passed in override the ones from the connection string. if (info != null) { info.stringPropertyNames().stream().forEach(k -> this.properties.put(PropertyKey.normalizeCase(k), info.getProperty(k))); } // Collect properties from additional sources. setupPropertiesTransformer(); expandPropertiesFromConfigFiles(this.properties); injectPerTypeProperties(this.properties); } ``` 5、当然参数值,还有其它的设置方式,比如expandPropertiesFromConfigFiles方法里面就是在是预置配置在com/mysql/cj/configurations/xxx.properties里面,例如url中增加&useConfigs=fullDebug,就可以在参数中增加如下四个参数; 还有一些其它的配置,比如dbname,既可以配置在port/后面,也可以以参数的形式配置 ``` profileSQL=true gatherPerfMetrics=true useUsageAdvisor=true logSlowQueries=true explainSlowQueries=true ``` 最终所有这些参数会封装成一个HostInfo对象 ##### 参数名与取值约束 1、建立连接的时候,会创建一个com.mysql.cj.jdbc.ConnectionImpl对象,它有两个属性:HostInfo(hostInfo为我们声明的参数),和PropertySet系统参数 2、所有PropertySet配置的属性名称,都必须是com.mysql.cj.conf.PropertyKey类中定义的名字 3、所有PropertySet配置的属性值的设置规则,都必须是com.mysql.cj.conf.PropertyDefinitions#PROPERTY_KEY_TO_PROPERTY_DEFINITION中定义的规则 值的规则有,boolean, enum, string,int,long等几种PropertyDefinition 4、属性值的用com.mysql.cj.conf.RuntimeProperty保存 其中UML关系如下: 连接与参数属性 ![jdbc连接与参数.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202106/29/204854gwmvuenjbi0ze1pm.png) 参数属性名与属性值 ![jdbc参数值定义.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202106/29/204906tl7u2470nlu4merd.png) 连接相关的对象 ![session与连接.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202106/29/205036fm8kmzxrzesoxs8i.png) ##### 参数使用 1、TCP连接 创建连接默认是通过com.mysql.cj.protocol.StandardSocketFactory做TCP连接,当然也可以通过socketFactory参数来配置 其中给socket配置的时候,使用到了tcpNoDelay,tcpKeepAlive,tcpRcvBuf,tcpSndBuf,tcpTrafficClass几个参数 连接地址使用到了ip, port参数 连接参数使用了connectTimeout和socketTimeout 可以通过useReadAheadInput,useUnbufferedInput两个bool参数决定使用哪种输入流 如果单次连接失败,还会根据initialTimeout(int),maxReconnects(int)来重试 2、协议连接 协议连接配置是在com.mysql.cj.protocol.a.NativeProtocol里面做的, 其中使用到useNanosForElapsedTime,maintainTimeStats(bool),maxQuerySizeToLog(int),autoSlowLog(bool),,maxAllowedPacket(int),profileSQL(bool),autoGenerateTestcaseScript(bool),useServerPrepStmts(bool), 慢查询相关logSlowQueries(bool),slowQueryThresholdMillis(int),useNanosForElapsedTime(bool),slowQueryThresholdNanos(int) 3、读取服务端参数 首先读取一个服务端发来的数据包,把服务端的参数设置到com.mysql.cj.protocol.a.NativeCapabilities对象里去,包括protocolVersion,serverVersion,threadId,seed,flag,capabilityFalgs,serverDefaultCollationIndex,authPluginDataLength 其中capabilityFalgs为服务端参数集,具体值及其意思,可以从com.mysql.cj.protocol.a.NativeServerSession中的那些值判断看出来 ``` public static final int CLIENT_LONG_PASSWORD = 0x00000001; /* new more secure passwords */ public static final int CLIENT_FOUND_ROWS = 0x00000002; public static final int CLIENT_LONG_FLAG = 0x00000004; /* Get all column flags */ public static final int CLIENT_CONNECT_WITH_DB = 0x00000008; public static final int CLIENT_COMPRESS = 0x00000020; /* Can use compression protcol */ public static final int CLIENT_LOCAL_FILES = 0x00000080; /* Can use LOAD DATA LOCAL */ public static final int CLIENT_PROTOCOL_41 = 0x00000200; // for > 4.1.1 public static final int CLIENT_INTERACTIVE = 0x00000400; public static final int CLIENT_SSL = 0x00000800; public static final int CLIENT_TRANSACTIONS = 0x00002000; // Client knows about transactions public static final int CLIENT_RESERVED = 0x00004000; // for 4.1.0 only public static final int CLIENT_SECURE_CONNECTION = 0x00008000; public static final int CLIENT_MULTI_STATEMENTS = 0x00010000; // Enable/disable multiquery support public static final int CLIENT_MULTI_RESULTS = 0x00020000; // Enable/disable multi-results public static final int CLIENT_PS_MULTI_RESULTS = 0x00040000; // Enable/disable multi-results for server prepared statements public static final int CLIENT_PLUGIN_AUTH = 0x00080000; public static final int CLIENT_CONNECT_ATTRS = 0x00100000; public static final int CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = 0x00200000; public static final int CLIENT_CAN_HANDLE_EXPIRED_PASSWORD = 0x00400000; public static final int CLIENT_SESSION_TRACK = 0x00800000; public static final int CLIENT_DEPRECATE_EOF = 0x01000000; ``` 4、配置客户端参数 根据capabilityFalgs及propertySet的值来设置clientParams的值,包括useCompression,createDatabaseIfNotExist,useAffectedRows,allowLoadLocalInfile,interactiveClient,allowMultiQueries,disconnectOnExpiredPasswords,connectionAttributes, 根据capabilityFalgs及propertySet的来useInformationSchema,sslMode做一些校验 还会使用defaultAuthenticationPlugin,disabledAuthenticationPlugins,authenticationPlugins,serverRSAPublicKeyFile,allowPublicKeyRetrieval来做认证插件相关配置 认证阶段,还会使用到user, password, database的信息 认证完之后,还会根据useCompression,traceProtocol,enablePacketDebug,packetDebugBufferSize来配置本地的环境 5、设置session参数可以通过参数sessionVariables来配置 ``` // public void setSessionVariables() { String sessionVariables = getPropertySet().getStringProperty(PropertyKey.sessionVariables).getValue(); if (sessionVariables != null) { List variablesToSet = new ArrayList(); for (String part : StringUtils.split(sessionVariables, ",", "\"'(", "\"')", "\"'", true)) { variablesToSet.addAll(StringUtils.split(part, ";", "\"'(", "\"')", "\"'", true)); } if (!variablesToSet.isEmpty()) { StringBuilder query = new StringBuilder("SET "); String separator = ""; for (String variableToSet : variablesToSet) { if (variableToSet.length() > 0) { query.append(separator); if (!variableToSet.startsWith("@")) { query.append("SESSION "); } query.append(variableToSet); separator = ","; } } sendCommand(this.commandBuilder.buildComQuery(null, query.toString()), false, 0); } } } ``` 6、查询服务端参数 建立连接之后,会在com.mysql.cj.NativeSession里面请求服务端参数,并把参数存储到ServerSession中 ``` //com.mysql.cj.NativeSession#loadServerVariables if (versionMeetsMinimum(5, 1, 0)) { StringBuilder queryBuf = new StringBuilder(versionComment).append("SELECT"); queryBuf.append(" @@session.auto_increment_increment AS auto_increment_increment"); queryBuf.append(", @@character_set_client AS character_set_client"); queryBuf.append(", @@character_set_connection AS character_set_connection"); queryBuf.append(", @@character_set_results AS character_set_results"); queryBuf.append(", @@character_set_server AS character_set_server"); queryBuf.append(", @@collation_server AS collation_server"); queryBuf.append(", @@collation_connection AS collation_connection"); queryBuf.append(", @@init_connect AS init_connect"); queryBuf.append(", @@interactive_timeout AS interactive_timeout"); if (!versionMeetsMinimum(5, 5, 0)) { queryBuf.append(", @@language AS language"); } queryBuf.append(", @@license AS license"); queryBuf.append(", @@lower_case_table_names AS lower_case_table_names"); queryBuf.append(", @@max_allowed_packet AS max_allowed_packet"); queryBuf.append(", @@net_write_timeout AS net_write_timeout"); queryBuf.append(", @@performance_schema AS performance_schema"); if (!versionMeetsMinimum(8, 0, 3)) { queryBuf.append(", @@query_cache_size AS query_cache_size"); queryBuf.append(", @@query_cache_type AS query_cache_type"); } queryBuf.append(", @@sql_mode AS sql_mode"); queryBuf.append(", @@system_time_zone AS system_time_zone"); queryBuf.append(", @@time_zone AS time_zone"); if (versionMeetsMinimum(8, 0, 3) || (versionMeetsMinimum(5, 7, 20) && !versionMeetsMinimum(8, 0, 0))) { queryBuf.append(", @@transaction_isolation AS transaction_isolation"); } else { queryBuf.append(", @@tx_isolation AS transaction_isolation"); } queryBuf.append(", @@wait_timeout AS wait_timeout"); NativePacketPayload resultPacket = sendCommand(this.commandBuilder.buildComQuery(null, queryBuf.toString()), false, 0); Resultset rs = ((NativeProtocol) this.protocol).readAllResults(-1, false, resultPacket, false, null, new ResultsetFactory(Type.FORWARD_ONLY, null)); Field[] f = rs.getColumnDefinition().getFields(); if (f.length > 0) { ValueFactory vf = new StringValueFactory(this.propertySet); Row r; if ((r = rs.getRows().next()) != null) { for (int i = 0; i f.length; i++) { this.protocol.getServerSession().getServerVariables().put(f[i].getColumnLabel(), r.getValue(i, vf)); } } } } else { NativePacketPayload resultPacket = sendCommand(this.commandBuilder.buildComQuery(null, versionComment + "SHOW VARIABLES"), false, 0); Resultset rs = ((NativeProtocol) this.protocol).readAllResults(-1, false, resultPacket, false, null, new ResultsetFactory(Type.FORWARD_ONLY, null)); ValueFactory vf = new StringValueFactory(this.propertySet); Row r; while ((r = rs.getRows().next()) != null) { this.protocol.getServerSession().getServerVariables().put(r.getValue(0, vf), r.getValue(1, vf)); } } ``` 查询到这些数据之后,就可以做一些正常查询时候的设置与判断了
  • [问题求助] 【clickhouse产品】clickhouse jdbc 查询后短时间内程序崩溃
    【功能模块】clickhouse 20.6.6.7版本arm架构编译,系统为centos7.6 版本clickhouse jdbc 查询后短时间内程序崩溃【操作步骤&问题现象】1、使用clickhouse-client在后台进行操作没有任何问题,建表查询等操作都不会引起崩溃2、使用DBeaver jdbc连接clickhouse进行查询,查询后可以得出结果,但是短时间内clickhouse就会崩溃报错【截图信息】【日志信息】(可选,上传日志内容或者附件)(gdb) bt#0  0x0000ffff949d5238 in raise () from /lib64/libc.so.6#1  0x0000ffff949d68b0 in abort () from /lib64/libc.so.6#2  0x0000000008a6a030 in terminate_handler () at /opt/clickhouse/ClickHouse-20.6/ClickHouse/base/daemon/BaseDaemon.cpp:369#3  0x000000000abe402c in std::__terminate (func=<optimized out>) at /opt/clickhouse/ClickHouse-20.6/ClickHouse/contrib/libcxxabi/src/cxa_handlers.cpp:59#4  0x000000000abe3d60 in __cxxabiv1::__cxa_rethrow () at /opt/clickhouse/ClickHouse-20.6/ClickHouse/contrib/libcxxabi/src/cxa_exception.cpp:616#5  0x0000000005281174 in DB::getCurrentExceptionMessage (with_stacktrace=with_stacktrace@entry=true,     check_embedded_stacktrace=check_embedded_stacktrace@entry=false, with_extra_info=with_extra_info@entry=true)    at /opt/clickhouse/ClickHouse-20.6/ClickHouse/src/Common/Exception.cpp:249#6  0x0000000008a699e4 in terminate_handler () at /opt/clickhouse/ClickHouse-20.6/ClickHouse/base/daemon/BaseDaemon.cpp:376#7  0x000000000abe402c in std::__terminate (func=<optimized out>) at /opt/clickhouse/ClickHouse-20.6/ClickHouse/contrib/libcxxabi/src/cxa_handlers.cpp:59#8  0x000000000abe3ba0 in failed_throw (exception_header=0xfffdd4006330)    at /opt/clickhouse/ClickHouse-20.6/ClickHouse/contrib/libcxxabi/src/cxa_exception.cpp:152#9  __cxxabiv1::__cxa_throw (thrown_object=thrown_object@entry=0xfffdd40063b0, tinfo=0xda23398 <typeinfo for Poco::Net::NoMessageException>,     dest=0x9da8218 <Poco::Net::NoMessageException::~NoMessageException()>)    at /opt/clickhouse/ClickHouse-20.6/ClickHouse/contrib/libcxxabi/src/cxa_exception.cpp:283#10 0x0000000009d90c00 in Poco::Net::HTTPRequest::read (this=this@entry=0xfffdda7ce440, istr=...)    at /opt/clickhouse/ClickHouse-20.6/ClickHouse/contrib/poco/Net/src/HTTPRequest.cpp:213#11 0x0000000009d93de8 in Poco::Net::HTTPServerRequestImpl::HTTPServerRequestImpl (this=0xfffdda7ce440, response=..., session=..., pParams=<optimized out>)    at /opt/clickhouse/ClickHouse-20.6/ClickHouse/contrib/poco/Net/src/HTTPServerRequestImpl.cpp:43#12 0x0000000009dcea60 in Poco::Net::HTTPServerConnection::run (this=0xfffdd4005b80)    at /opt/clickhouse/ClickHouse-20.6/ClickHouse/contrib/poco/Net/src/HTTPServerConnection.cpp:69#13 0x0000000009dc99dc in Poco::Net::TCPServerConnection::start (this=this@entry=0xfffdd4005b80)    at /opt/clickhouse/ClickHouse-20.6/ClickHouse/contrib/poco/Net/src/TCPServerConnection.cpp:43#14 0x0000000009dc9ed0 in Poco::Net::TCPServerDispatcher::run (this=0x42a241e0)    at /opt/clickhouse/ClickHouse-20.6/ClickHouse/contrib/poco/Net/src/TCPServerDispatcher.cpp:114#15 0x0000000009efe818 in Poco::PooledThread::run (this=0x42a22470)    at /opt/clickhouse/ClickHouse-20.6/ClickHouse/contrib/poco/Foundation/src/ThreadPool.cpp:199#16 0x0000000009ef9d80 in Poco::ThreadImpl::runnableEntry (pThread=<optimized out>)    at /opt/clickhouse/ClickHouse-20.6/ClickHouse/contrib/poco/Foundation/include/Poco/SharedPtr.h:401#17 0x0000ffff94b97d38 in start_thread () from /lib64/libpthread.so.0#18 0x0000ffff94a7f690 in thread_start () from /lib64/libc.so.6(gdb) 
总条数:82 到第
上滑加载中