搜索词>>事物 耗时0.1410
  • Spring Boot 事物回滚实验

    1.开启事物通过注解开启@EnableTransactionManagement 提示: Spring Boot  以Spring 5.0为基础的版本无需注解自动开启事物2.测试事物首先查询数据库中的表​默认表中有一条数据;2.1默认不用事1.开启事物通过注解开启@EnableTransactionManagement 提示: Spring Boot  以Spring 5.0为基础的版本无需注解自动开启事物2.测试事物首先查询数据库中的表​默认表中有一条数据;2.1默认不用事物首先是不开启,创建两个数据代码如下:public void test() { SystemMenu m1=new SystemMenu(); m1.setName("test2"); m1.setTerminal("00"); menuExtMapper.insertSelective(m1); SystemMenu m2=new SystemMenu(); m2.setId(1);//重复ID 数据库会报错 m2.setName("test3"); m2.setTerminal("00"); }执行上面的代码:​通过控制台我们可以看到已经输出了 主键冲突的异常,我们再看看数据库:​通过数据库查询可以看到,test2数据已经存入数据库,test3肯定就是失败了,同时证明了默认没开启事物,遇到错误并不会回滚。2.2通过注解@Transactional开启事物首先我们将数据库还原,删除掉test2数据,如下:​程序代码如下:@Transactional public void test() { SystemMenu m1=new SystemMenu(); m1.setName("test2"); m1.setTerminal("00"); menuExtMapper.insertSelective(m1); SystemMenu m2=new SystemMenu(); m2.setId(1);//重复ID 数据库会报错 m2.setName("test3"); m2.setTerminal("00"); menuExtMapper.insertSelective(m2); }执行上方代码:​从控制台看,同样输出了主键冲突的错误,我们再看看数据库中的数据:​从数据可以看到,并没有数据存入。这里也就证明了事物开启成功,遇到错误就会同一回滚保证了数据的一致性。提示:默认情况下,@Transactional注解只对RuntimeException及其子类异常有效。证明上方的观点,下面以代码示例:@Transactional public void test() throws Exception { SystemMenu m1=new SystemMenu(); m1.setName("test2"); m1.setTerminal("00"); menuExtMapper.insertSelective(m1); if (1==1){ throw new Exception("EEEEEE"); } SystemMenu m2=new SystemMenu(); m2.setId(1);//重复ID 数据库会报错 m2.setName("test3"); m2.setTerminal("00"); menuExtMapper.insertSelective(m2); }上方代码,在处理数据中间主动抛出了Exception异常,执行代码观察控制台以及数据库:​控制台​数据库通过上方我们可以看到,抛出Exception的情况下事物并没有生效,test2已经存入数据库中。提示:可以通过注解@Transactional中的rollbackFor 来指定事物遇到那些异常会启用,同样也可以通过noRollbackFor指定那些异常不会滚例如下方的配置事物遇到所有的Exception都会回滚@Transactional(rollbackFor = {Exception.class})2.3 启用事物的方法内部调用其他方法出现异常会回滚吗首先我们蒋数据库恢复如下:​编写测试代码如下:@Transactional(rollbackFor = {Exception.class}) public void test() throws Exception { SystemMenu m1=new SystemMenu(); m1.setName("test2"); m1.setTerminal("00"); menuExtMapper.insertSelective(m1); oo(); } public void oo(){ SystemMenu m2=new SystemMenu(); m2.setId(1); m2.setName("test2"); m2.setTerminal("00"); menuExtMapper.insertSelective(m2); }执行代码,观察控制台和数据库:​控制台​数据库通过上方实验,我们可以看到事物生效了。所以内部调用其他方法出现异常同样会触发事物提示:调用的方法需没有标注注解@Transactional,如果标注注解会有几种情况,后方说明。2.4 启用事物的方法内部调用其他启用事物的方法编写测试代码1: @Transactional(rollbackFor = {Exception.class}) public void test() throws Exception { oo(); SystemMenu m1=new SystemMenu(); m1.setId(1); m1.setName("test2"); m1.setTerminal("00"); menuExtMapper.insertSelective(m1); } @Transactional public void oo(){ SystemMenu m2=new SystemMenu(); m2.setName("test3"); m2.setTerminal("00"); menuExtMapper.insertSelective(m2); }执行测试代码并观察控制台和数据库:​控制台​数据库通过上方实验,我们可以知道默认情况下,@Transactional注解的方法调用@Transactional注解的方法也是可以事物统一回滚的。注意:调用的方法上指定的Exception范围必须包含被调用方抛出异常的范围,否则可能出现不一致上方还涉及到一个知识点,事物的传播级别,默认情况下事物的传播级别为"REQUIRED",即:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。所以上方两个方法的事物会被整合成一个事物处理。@Transactional注解也可以通过propagation 来指定传播级别,如下:@Transactional(propagation = Propagation.REQUIRED)事物传播的几个级别分别为:REQUIRED(@Transactional注解默认值): 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。SUPPORTS:前方法不需要事务上下文,但是如果存在当前事务的话,那么这个方法会在这个事务中运行。MANDATORY:该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常。不会主动开启一个事务。REQUIRES_NEW:前方法必须运行在它自己的事务中。一个新的事务将被启动,如果存在当前事务,在该方法执行期间,当前事务会被挂起(如果一个事务已经存在,则先将这个存在的事务挂起)。如果使用JTATransactionManager的话,则需要访问TransactionManager。NOT_SUPPORTED:该方法不应该运行在事务中,如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager。NEVER:表示当前方法不应该运行在事务上下文中,如果当前正有一个事务在运行,则会抛出异常。NESTED:如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与REQUIRED一样。嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。(未完待续)后续继续更新实验....
  • Spring框架事物管理入门教程@Transactional注解使用

    Spring框架是一个非常智能的框架,本文主要讲解spring框架中事物的处理。使用@Transactional注解来标注事物的处理类型等属性<h2>事务有四个特性:ACID</h2> <ul> <li>原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。</li> <li>一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。</li> <li>隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。</li> <li>持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。</li> </ul> Spring框架事务管理涉及的接口的联系如下:<br /> <img alt="1" class="img-thumbnail" src="/assist/images/blog/6bca1098-a091-45f7-8eee-ce32549a556f.jpg" style="height:413px; width:945px" /><br /> @Transactional的属性<br />   <table border="1" class="table table-bordered table-hover"> <tbody> <tr> <td>属性名 </td> <td>类型 </td> <td>说明 </td> </tr> <tr> <td>isolation </td> <td>枚举org.springframework.transaction.annotation.Isolation的值 </td> <td>事务隔离级别 ,默认值Isolation.DEFAULT</td> </tr> <tr> <td>noRollbackFor </td> <td>Class<? extends Throwable>[] </td> <td>一组异常类,遇到时不回滚。默认为{}</td> </tr> <tr> <td>noRollbackForClassName </td> <td>Stirng[] </td> <td>一组异常类名,遇到时不回滚,默认为{}</td> </tr> <tr> <td>propagation </td> <td>枚举org.springframework.transaction.annotation.Propagation的值 </td> <td>事务传播行为 ,默认值 Propagation.REQUIRED</td> </tr> <tr> <td>readOnly </td> <td>boolean </td> <td>事务读写性 ,默认false</td> </tr> <tr> <td>rollbackFor </td> <td>Class<? extends Throwable>[] </td> <td>一组异常类,遇到时回滚 </td> </tr> <tr> <td>rollbackForClassName </td> <td>Stirng[] </td> <td>一组异常类名,遇到时回滚 </td> </tr> <tr> <td>timeout </td> <td>int </td> <td>超时时间,以秒为单位 ,ransactionDefinition.TIMEOUT_DEFAULT</td> </tr> <tr> <td>value </td> <td>String </td> <td>可选的限定描述符,指定使用的事务管理器 </td> </tr> </tbody> </table> <h2><br /> 事务传播行为类型:</h2> <table border="1" class="table table-bordered table-hover"> <tbody> <tr> <td>事务传播行为类型</td> <td>说明</td> </tr> <tr> <td>PROPAGATION_REQUIRED</td> <td>如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。<span style="color:#ff0000">默认值</span></td> </tr> <tr> <td>PROPAGATION_SUPPORTS</td> <td>支持当前事务,如果当前没有事务,就以非事务方式执行。</td> </tr> <tr> <td>PROPAGATION_MANDATORY</td> <td>使用当前的事务,如果当前没有事务,就抛出异常。</td> </tr> <tr> <td>PROPAGATION_REQUIRES_NEW</td> <td>新建事务,如果当前存在事务,把当前事务挂起。</td> </tr> <tr> <td>PROPAGATION_NOT_SUPPORTED</td> <td>以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。</td> </tr> <tr> <td>PROPAGATION_NEVER</td> <td>以非事务方式执行,如果当前存在事务,则抛出异常。</td> </tr> <tr> <td>PROPAGATION_NESTED</td> <td>如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类 似的操作。</td> </tr> </tbody> </table> <h1><br /> 事物隔离级别</h1>   <table class="table table-bordered table-hover" border="0" cellpadding="0" cellspacing="0" style="width:888px"> <tbody> <tr> <td>隔离级别          </td> <td>隔离级别的值</td> <td>导致的问题</td> </tr> <tr> <td>Read-Uncommitted</td> <td>0     </td> <td>导致脏读</td> </tr> <tr> <td>Read-Committed   </td> <td>1     </td> <td>避免脏读,允许不可重复读和幻读</td> </tr> <tr> <td>Repeatable-Read   </td> <td>2     </td> <td>避免脏读,不可重复读,允许幻读</td> </tr> <tr> <td>Serializable   </td> <td>3     </td> <td>串行化读,事务只能一个一个执行,避免了脏读、不可重复读、幻读。执行效率慢,使用时慎重</td> </tr> </tbody> </table> <br /> <strong>名词解释:</strong> <ul> <li><strong>脏读:</strong>一事务对数据进行了增删改,但未提交,另一事务可以读取到未提交的数据。如果第一个事务这时候回滚了,那么第二个事务就读到了脏数据。</li> <li><strong>不可重复读</strong>:一个事务中发生了两次读操作,第一次读操作和第二次操作之间,另外一个事务对数据进行了修改,这时候两次读取的数据是不一致的。</li> <li><strong>幻读</strong>:第一个事务对一定范围的数据进行批量修改,第二个事务在这个范围增加一条数据,这时候第一个事务就会丢失对新增数据的修改。</li> </ul> <br /> 提示: <ul> <li>隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。</li> <li>大多数的数据库默认隔离级别为 Read Commited,比如 SqlServer、Oracle</li> <li>少数数据库默认隔离级别为:Repeatable Read 比如: MySQL InnoDB</li> </ul> <h3><br /> Spring 中的隔离设置</h3> ISOLATION_DEFAULT     这是个 PlatfromTransactionManager <span style="color:#ff0000">默认的隔离级别</span>,使用数据库默认的事务隔离级别。另外四个与 JDBC 的隔离级别相对应。<br /> ISOLATION_READ_UNCOMMITTED     这是事务最低的隔离级别,它充许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。<br /> ISOLATION_READ_COMMITTED     保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。<br /> ISOLATION_REPEATABLE_READ     这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。<br /> ISOLATION_SERIALIZABLE     这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。<br /> <br /> <span style="color:#ff0000"><strong>注意:</strong></span><br /> <strong><span style="color:#16a085">1.使用注解事物,必须在spring配置中开启事物<br /> 2.@Transactional注解可以标注在类和方法上,也可以标注在定义的接口和接口方法上。<br /> 如果我们在接口上标注@Transactional注解,会留下这样的隐患:因为注解不能被继承,所以业务接口中标注的@Transactional注解不会被业务实现类继承。所以可能会出现不启动事务的情况。所以,spring建议我们将@Transaction注解在实现类上。<br /> 在方法上的@Transactional注解会覆盖掉类上的@Transactional。</span></strong>
  • 分布式系统架构_分布式事物_分布式系统原理

    分布式系统架构_分布式事物_分布式系统原理<h2>引言</h2>     随着大数据时代的到来。大多数之前业务系统已经无法承受大数据带来的冲击。各个大公司小公司都在考虑如何优化自己的业务系统。提高业务能力减少成本支出。随之就引出了 <strong>分布式系统</strong>。 <h2>一.为什么要进行<strong>分布式系统架构</strong>?</h2> <p>  大多数的开发者大多数的系统可能从来没接触过分布式系统,也根本没必要进行分布式系统架构,为什么?因为在访问量或者QPS没有达到单台机器的性能瓶颈的时候,根本没必要进行分布式架构。那如果业务量上来了,一般会怎么解决呢?</p> <p>    首先考虑的就是机器升级。机器配置的垂直扩展,首先要找到当前性能的瓶颈点,是CPU,是内存,是硬盘,还是带宽。砸钱加CPU,砸钱换SSD硬盘,砸钱换1T内存,这通常是解决问题最直接也最高效的方法。带宽不够?加带宽,1G不够用100G。CPU 8核不够?搞32核96核。这是绝大多数公司能思考到的第一个方案,也是最高效最快最安全的方法,立竿见影。</p> <p>    其次就是系统拆分,将所提供服务的主流程以及支线流程梳理出来,按照流程进行系统拆分。如同一棵树,核心业务作为主干流程,其他系统按照需要进行拆分,如同树的开枝散叶。所采取的方式有这么一些,按前后端进行拆分,按照领域拆分,按团队拆分,当然通常来说这些拆分基本都要跟着组织架构走。</p> <p>    再不行就进行技术升级,更换更加高效或者场景适合的技术。比如从 Oracle 更换到HBase。从A数据库连接池更换到B数据库连接池。技术的变革对于业务量的支持也是非常巨大的,同一台机器不同的技术,效能发挥的程度可以说有天壤之别。</p> <p>    最后的最后手段才会考虑分布式架构,实在是砸不出这么多钱了,实在是没办法了。因为分布式架构肯定会带来非常多非常多的一致性问题,原本只需要访问一台机器,现在需要访问N台,那么这N台机器的一致性怎么保证,以前撑死搞个主从备份就算完了,定时同步一下数据就好,现在N台设备的数据怎么管理,甚至这个集群本身怎么管理,都会成为一个致命的问题。</p> <p>    所以只有等业务量到达一定程度了,单台机器扛不住了,才会开始堆钱升级机器,系统拆分,换技术,继续堆钱升级机器,系统拆分...周而复始,发现成本太高或者技术已经到达上线了。最后没办法,就选择分布式架构了。</p> <p>但是分布式架构的优势也是明显的,用一群低廉的设备,来提供一个高性能高吞吐量的稳定的系统,下面开始说说常见的分布式集群的架构。</p> <h2>二.分布式系统架构的几种模式</h2> <h3>2.1纯负载均衡模式</h3>     在集群前面,前置一个流量分发的组件进行流量分发,整个集群的机器提供无差别的服务,这在常见的 web 服务器中是最最常见的。目前比较主流的方式就是整个集群机器上云,根据实时的调用量进行云服务器弹性伸缩。常见的负载均衡有硬件层面的 F5、软件层面的 nginx 等。<br /> <img alt="传统负载均衡" class="img-thumbnail" src="/assist/images/blog/c9ccf03ca8b54ac4852bbe8f1abf10b7.png" /> <h3>2.2<strong>领导选举型</strong></h3> 整个集群的消息都会转发到集群的领导这里,是一种 master-slavers,区别只是这个 master 是被临时选举出来的,一旦 master 宕机,集群会立刻选举出一个新的领导,继续对外提供服务。使用领导选举型架构的典型的应用有 ElasticSearch,zookeeper。<br /> <img alt="领导选举" class="img-thumbnail" src="/assist/images/blog/06857af58c4f4b31a75e5b4ac28f19ed.jpg" /> <h3><strong>2.3区块链型</strong><br /> 整个集群的每一个节点都可以进行记录,但是记录的内容要得到整个集群 N 个机器的认可才是合法的。典型的应用有 Bit Coin,以及 Hyperledger。</h3> <img alt="整个集群的消息都会转发到集群的领导这里,是一种 master-slavers,区别只是这个 master 是被临时选举出来的,一旦 master 宕机,集群会立刻选举出一个新的领导,继续对外提供服务。使用领导选举型架构的典型的应用有 ElasticSearch,zookeeper。" class="img-thumbnail" src="/assist/images/blog/78cd41be88fa409c9c11e77c5ef193ff.jpg" /> <h3>2.4<strong>master-slaver型</strong></h3> 整个集群以某台 master 为中枢,进行集群的调度。交互是这样,一般会把所有的管理类型的数据放到 master 上,而把具体的数据放到 slaver 上,实际进行调用的时候,client 先调用 master 获取数据所存放的 server 的 信息,再自行跟 slave 进行交互。典型的系统有 Hadoop。集群,HBase 集群,Redis 集群等。<br /> <img alt="整个集群以某台 master 为中枢,进行集群的调度。交互是这样,一般会把所有的管理类型的数据放到 master 上,而把具体的数据放到 slaver 上,实际进行调用的时候,client 先调用 master 获取数据所存放的 server 的 信息,再自行跟 slave 进行交互。典型的系统有 Hadoop。集群,HBase 集群,Redis 集群等。" class="img-thumbnail" src="/assist/images/blog/68bb6e12564d491da16f3569088b7896.jpg" /> <h3>2.5<strong>规则型一致性Hash</strong></h3> 这种架构类型一般出现在数据库分库分表的设计中。按照规则进行分库分表,在查询之前使用规则引擎进行库和表的确认,再对具体的应用进行访问。为什么要用一致性 Hash ?其实用什么都可以,只是对于这类应用来说一致性 Hash 比较常见而已。<br /> <img alt="这种架构类型一般出现在数据库分库分表的设计中。按照规则进行分库分表,在查询之前使用规则引擎进行库和表的确认,再对具体的应用进行访问。为什么要用一致性 Hash ?其实用什么都可以,只是对于这类应用来说一致性 Hash 比较常见而已。" class="img-thumbnail" src="/assist/images/blog/2735e320027f4f91bbd9319fda6f5b93.jpg" /><br />  
  • spring boot mybatis 整合_spring boot mybatis3 事物配置

    引言    通过之前spring boot mybatis 整合的讲解: spring boot mybaties整合  (spring boot mybaties 整合 基于Java注解方式写sql,无需任何得mapper xml文件)s引言    通过之前spring boot mybatis 整合的讲解: spring boot mybaties整合  (spring boot mybaties 整合 基于Java注解方式写sql,无需任何得mapper xml文件)spring boot mybatis 整合_spring boot与mybaties的使用  (spring boot mybaties 整合 xml mapper方式,也是实际应用最多得方式) 我们对于spring boot mybaties 整合有了一个基础的认知。这里主要正对上面得两篇文章中spring boot mybaties整合讲解得一个扩展学习,事物的配置,整合到spring 的事物控制中。一.环境准备 本博客讲沿用上面的项目进行进一步讲解二.实战编码2.1 spring boot 核心配置文件application.properties#==================DataSource Config Start================== #默认采用Tomcat-jdbc-pool性能和并发最好,注意查看maven依赖中是否有tomcat-jdbc #name #spring.datasource.name=test #url #spring.datasource.url=jdbc:sqlserver://192.168.xxx.xxx;instanceName=sql_03;DatabaseName=edu;integratedSecurity=false spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8 #DriverClass #spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver spring.datasource.tomcat.driver-class-name=com.mysql.jdbc.Driver #DB username spring.datasource.tomcat.username=root #DB password spring.datasource.tomcat.password=root #最大连接数量 spring.datasource.tomcat.max-active=150 #最大闲置连接数量 spring.datasource.tomcat.max-idle=20 #最大等待时间 #spring.datasource.tomcat.max-wait=5000 #==================DataSource Config End================== #==================mybaties Config Start================== #ORM Bean Package mybatis.type-aliases-package=com.example.pojo mybatis.mapper-locations=classpath:/mapper/*.xml #打印mybatiesSql语句 logging.level.com.example.mapper=DEBUG #==================mybaties Config End ================== #模板引擎配置缓存为FALSE。开发调试用 spring.thymeleaf.cache=false 这里注意关注数据连接配置和mybaties的xml mapper文件配置。2.2spring boot mybaties 整合 事物关键配置 MyBatiesConfig.javapackage com.example.config; import javax.sql.DataSource; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * mybaties配置扫描mapper路径 * * @author leftso * */ @Configuration @MapperScan(basePackages = { "com.example.mapper" }) /** 注意,这个注解是扫描mapper接口不是xml文件,使用xml模式必须在配置文件中添加xml的配置 **/ @EnableTransactionManagement /** * 启用事物管理 ,在需要事物管理的service类或者方法上使用注解@Transactional **/ public class MyBatiesConfig { @Autowired private DataSource dataSource; /** * 配合注解完成事物管理 * * @return */ @Bean public PlatformTransactionManager annotationDrivenTransactionManager() { return new DataSourceTransactionManager(dataSource); } } 注意必须把当前的数据源配置进入spring的注解事物管理器。否则通过spring框架的注解标签@Transactional是不会有事物作用的。提示:spring boot 2.1.4.RELEASE 版本无需配置PlatformTransactionManager 也能起作用,也就说仅需要一个注解@EnableTransactionManagementSpring boot 2.x (Spring 5.0为基础的情况)无需使用@EnableTransactionManagement注解,spring boot 项目内部已经启用三.事物演示3.1编写测试代码package com.example.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.example.mapper.UserMapper; import com.example.pojo.User; @RunWith(SpringRunner.class) @SpringBootTest public class TransactionalTest { @Autowired private UserMapper userMapper; @Test public void name() { User user=new User("leftso", "男", 1); userMapper.insert(user); int t=1/0; System.out.println(t); } } 执行前查询数据库:​执行测试代码并观察eclipse的控制台和数据库的数据查询结果:​​很明显在报错的情况下,数据还是插入进了数据库。这并不是我们正常业务想要的结果。3.2编辑测试代码,添加spring框架的事物注解package com.example.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; import com.example.mapper.UserMapper; import com.example.pojo.User; @RunWith(SpringRunner.class) @SpringBootTest public class TransactionalTest { @Autowired private UserMapper userMapper; @Test @Transactional public void name() { User user=new User("测试哈哈", "女", 2); userMapper.insert(user); int t=1/0; System.out.println(t); } } 执行代码并观察eclipse和数据库:​​这次的操作姿势似乎对了。在报错的情况下数据并没有插入数据库。我们仔细观察spring 控制台输出的日志可以发现事物已经在spring的控制下回滚了。​从上图也可以看到回滚的日志
  • Spring Boot Redis 秒杀实现

    简述本文主要通过一个简单的例子模拟实现秒杀情景,其中主要使用Redis事物进行实现spring boot为提供方便的环境简述本文主要通过一个简单的例子模拟实现秒杀情景,其中主要使用Redis事物进行实现spring boot为提供方便的环境。首先导入redis依赖pom.xml文件<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.leftso.demo</groupId> <artifactId>demo-redis-seckill</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo-redis-seckill</name> <description>Redis 实现产品秒杀</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.0.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>Redispool配置配置redis的连接池,这个根据自己需求改。这里测试用。package com.leftso.demo.demoredisseckill; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; @Configuration public class JedisConfig { @Bean public JedisPool jedisPool(){ JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); // 设置配置 jedisPoolConfig.setMaxTotal(1024); jedisPoolConfig.setMaxIdle(100); jedisPoolConfig.setMaxWaitMillis(100); jedisPoolConfig.setTestOnBorrow(false);//jedis 第一次启动时,会报错 jedisPoolConfig.setTestOnReturn(true); JedisPool pool=new JedisPool(jedisPoolConfig,"127.0.0.1",6379); return pool; } } 写一个秒杀的业务处理实现package com.leftso.demo.demoredisseckill.service; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.Transaction; import java.util.List; public class Seckill implements Runnable { private JedisPool jedisPool; private String userName; private String productKey; public Seckill(JedisPool jedisPool, String userName, String productKey) { this.jedisPool = jedisPool; this.userName = userName; this.productKey = productKey; } @Override public void run() { Jedis jedis=jedisPool.getResource(); try { jedis.watch(productKey); String val=jedis.get(productKey); int valInt=Integer.valueOf(val); if (valInt>=1){ Transaction tx=jedis.multi(); tx.incrBy(productKey,-1);//原子操作 List<Object> list=tx.exec(); if (list==null||list.isEmpty()){ System.out.println("用户:"+userName+" 抢购失败。"); this.run();//再抢 }else{ System.out.println("用户:"+userName+ " 抢购成功!!!"); // jedis.setnx(productKey,) jedis.rpush(productKey+"user",userName);//成功用户添加入队列 } }else{ System.out.println("商品已抢购完毕-------"); } }catch (Exception e){ e.printStackTrace(); } } } 最后模拟访问package com.leftso.demo.demoredisseckill; import com.leftso.demo.demoredisseckill.service.Seckill; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import redis.clients.jedis.JedisPool; import java.util.ArrayList; import java.util.List; @RunWith(SpringRunner.class) @SpringBootTest public class RedisSeckillTest { private final static Logger logger = LoggerFactory.getLogger(RedisSeckillTest.class); @Autowired JedisPool jedisPool; String productKey="SSSSSSKEY"; int productNum=10; @Before public void before(){ jedisPool.getResource().set(productKey,10+"");//设置产品默认库存数量 while (jedisPool.getResource().lpop(productKey+"user")!=null){ }//清空秒杀成功人用户列表 //end } @After public void after(){ String num=jedisPool.getResource().get(productKey); System.out.println("剩余库存:"+num); } @Test public void contextLoads() { try { for (int i = 0; i < 100; i++) { //每个用户件数 Thread t = new Thread(new Seckill(jedisPool,"用户"+i,productKey)); t.start(); } long size=jedisPool.getResource().llen(productKey+"user"); while (true){ if (size==productNum){ break; }else{ size=jedisPool.getResource().llen(productKey+"user"); } } List<String> successUsers=new ArrayList<>(); String user=jedisPool.getResource().lpop(productKey+"user"); while (user!=null){ successUsers.add(user); user=jedisPool.getResource().lpop(productKey+"user"); } System.out.println("活动结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>活动结束"); System.out.println("获奖名单:"+successUsers); Thread.currentThread().sleep(2000); } catch (Exception e) { e.printStackTrace(); } } } 执行单元测试,查看结果:.............................. 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 商品已抢购完毕------- 活动结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>活动结束 获奖名单:[用户66, 用户2, 用户8, 用户10, 用户56, 用户78, 用户33, 用户58, 用户16, 用户87] 剩余库存:0 Process finished with exit code 0您有任何想法欢迎评论区留言讨论,谢谢
  • java常见面试题

    java常见面试题之冒泡排序<h2>引言</h2> 记录常见的java面试题及解决思路方法。 <h2>java冒泡排序</h2> <pre> <code class="language-java">package net.xqlee.project; public class TestMaoPao { public static void main(String[] args) { int[] arr = new int[] { 0, 2, 3, 7, -1, 90, 290, -1, 100 }; int swap = 0; for (int i = 0; i < arr.length; i++) { for (int j = i; j < arr.length; j++) { if (arr[j] > arr[i]) { swap = arr[i]; arr[i] = arr[j]; arr[j] = swap; } } } System.out.println(arr[0]); } } </code></pre> <br /> 冒泡排序原理: 从第一个开始,与后面的每一个比较,找到比他大/小的就交换,一轮下来就可以将最大/小值放在索引的第一个位置,后面以此类推。 <h2><strong>servlet执行流程</strong></h2>   客户端发出http请求,web服务器将请求转发到servlet容器,servlet容器解析url并根据web.xml找到相对应的servlet,并将request、response对象传递给找到的servlet,servlet根据request就可以知道是谁发出的请求,请求信息及其他信息,当servlet处理完业务逻辑后会将信息放入到response并响应到客户端。 <h2><strong>springMVC的执行流程</strong></h2>   springMVC是由dispatchservlet为核心的分层控制框架。首先客户端发出一个请求web服务器解析请求url并去匹配dispatchservlet的映射url,如果匹配上就将这个请求放入到dispatchservlet,dispatchservlet根据mapping映射配置去寻找相对应的handel,然后把处理权交给找到的handel,handel封装了处理业务逻辑的代码,当handel处理完后会返回一个逻辑视图modelandview给dispatchservlet,此时的modelandview是一个逻辑视图不是一个正式视图,所以dispatchservlet会通过viewresource视图资源去解析modelandview,然后将解析后的参数放到view中返回到客户端并展现。 <h2><strong>Java设计模式思想(单列模式,工厂模式,策略模式,共23种设计模式)</strong></h2> <ul> <li>a) 单例模式:单例模式核心只需要new一个实例对象的模式,比如数据库连接,在线人数等,一些网站上看到的在线人数统计就是通过单例模式实现的,把一个计时器存放在数据库或者内存中,当有人登陆的时候取出来加一再放回去,有人退出登陆的时候取出来减一再放回去,但是当有两个人同时登陆的时候,会同时取出计数器,同时加一,同时放回去,这样的话数据就会错误,所以需要一个全局变量的对象给全部人使用,只需要new出一个实例对象,这就是单例模式的应用,并且单例模式节省资源,因为它控制了实例对象的个数,并有利于gc回收。</li> <li>b) 策略模式:就是将几个类中公共的方法提取到一个新的类中,从而使扩展更容易,保证代码的可移植性,可维护性强。比如有个需求是写鸭子对象,鸭子有叫,飞,外形这三种方法,如果每个鸭子类都写这三个方法会出现代码的冗余,这时候我们可以把鸭子中的叫,飞,外形这三个方法提取出来,放到鸭父类中,让每个鸭子都继承这个鸭父类,重写这三个方法,这样封装的代码可移植性强,当用户提出新的需求比如鸭子会游泳,那么对于我们oo程序员来讲就非常简单了我们只需要在鸭父类中加一个游泳的方法,让会游泳的鸭子重写游泳方法就可以了。</li> <li>c) 工厂模式:简单的工厂模式主要是统一提供实例对象的引用,通过工厂模式接口获取实例对象的引用。比如一个登陆功能,后端有三个类,controller类,interface类,实现接口的实现类。当客户端发出一个请求,当请求传到controller类中时,controller获取接口的引用对象,而实现接口的实现类中封装好了登陆的业务逻辑代码。当你需要加一个注册需求的时候只需要在接口类中加一个注册方法,实现类中实现方法,controller获取接口的引用对象即可,不需要改动原来的代码,这种做法是的可拓展性强。</li> </ul> <h2><strong>内部类与外部类的调用</strong></h2> <ul> <li>a) 内部类可以直接调用外部类包括private的成员变量,使用外部类引用的this.关键字调用即可</li> <li>b) 而外部类调用内部类需要建立内部类对象</li> </ul> <h2><strong>AOP与IOC的概念(即spring的核心)</strong></h2> <ul> <li>a) IOC:Spring是开源框架,使用框架可以使我们减少工作量,提高工作效率并且它是分层结构,即相对应的层处理对应的业务逻辑,减少代码的耦合度。而spring的核心是IOC控制反转和AOP面向切面编程。IOC控制反转主要强调的是程序之间的关系是由容器控制的,容器控制对象,控制了对外部资源的获取。而反转即为,在传统的编程中都是由我们创建对象获取依赖对象,而在IOC中是容器帮我们创建对象并注入依赖对象,正是容器帮我们查找和注入对象,对象是被获取,所以叫反转。</li> <li>b) AOP:面向切面编程,主要是管理系统层的业务,比如日志,权限,事物等。AOP是将封装好的对象剖开,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为切面(aspect),切面将那些与业务逻辑无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。</li> </ul> <h2><strong>Arraylist与linkedlist的区别</strong></h2> a) 都是实现list接口的列表,arraylist是基于数组的数据结构,linkedlist是基于链表的数据结构,当获取特定元素时,ArrayList效率比较快,它通过数组下标即可获取,而linkedlist则需要移动指针。当存储元素与删除元素时linkedlist效率较快,只需要将指针移动指定位置增加或者删除即可,而arraylist需要移动数据。 <h2><strong>数据库优化</strong></h2> <ul> <li>a) 选择合适的字段,比如邮箱字段可以设为char(6),尽量把字段设置为notnull,这样查询的时候数据库就不需要比较null值</li> <li>b) 使用关联查询( left join on)查询代替子查询</li> <li>c) 使用union联合查询手动创建临时表</li> <li>d) 开启事物,当数据库执行多条语句出现错误时,事物会回滚,可以维护数据库的完整性</li> <li>e) 使用外键,事物可以维护数据的完整性但是它却不能保证数据的关联性,使用外键可以保证数据的关联性</li> <li>f) 使用索引,索引是提高数据库性能的常用方法,它可以令数据库服务器以比没有索引快的多的速度检索特定的行,特别是对于max,min,order by查询时,效果更明显</li> <li>g) 优化的查询语句,绝大多数情况下,使用索引可以提高查询的速度,但如果sql语句使用不恰当的话,索引无法发挥它的特性。</li> </ul> <h2>Java集合类框架的基本接口有哪些</h2> Collection集合接口,<br /> List、set实现Collection接口,<br /> arraylist、linkedlist,vector实现list接口,<br /> stack继承vector,Map接口,<br /> hashtable、hashmap实现map接口 <h2><strong>事物的理解</strong></h2> <ul> <li>a) 事物具有原子性,一致性,持久性,隔离性</li> <li>b) 原子性:是指在一个事物中,要么全部执行成功,要么全部失败回滚。</li> <li>c) 一致性:事物执行之前和执行之后都处于一致性状态</li> <li>d) 持久性:事物多数据的操作是永久性</li> <li>e) 隔离性:当一个事物正在对数据进行操作时,另一个事物不可以对数据进行操作,也就是多个并发事物之间相互隔离。</li> </ul> <h2><strong>对象的创建</strong></h2> <ul> <li>a) 遇到一个新类时,会进行类的加载,定位到class文件</li> <li>b) 对所有静态成员变量初始化,静态代码块也会执行,而且只在类加载的时候执行一次</li> <li>c) New 对象时,jvm会在堆中分配一个足够大的存储空间</li> <li>d) 存储空间清空,为所有的变量赋默认值,所有的对象引用赋值为null</li> <li>e) 根据书写的位置给字段一些初始化操作</li> <li>f) 调用构造器方法(没有继承)</li> </ul> <h2>JAVA WEB 九大内置对象</h2>   九大内置对象分别是: request response session application out page config exception pageContent<br /> <br />   其中: <ul> <li>request  response  out page config exception pageContent对象的有效范围是当前页面的应用 </li> <li>session 有效范围是当前会话(当前客户端的所有页面)</li> <li>application 有效范围是整个应用程序,只要服务器不关闭对象就有效</li> </ul> ====================================================================<br /> request<br /> ====================================================================<br /> request.getParameter();获得用户提交的表单信息<br /> request.setCharacterEncoding("UTF-8");设置请求编码,防止乱码<br /> request.setAttribute("Unmae", new Object());将数据保存到request范围内的变量中<br /> request.forward(String Url);转发<br /> request.getRequestURL();获得当前页的IE地址<br /> request.getHeader("resref");获得请求也的IE地址<br /> request.getRemoteAddr();获得用户的IP地址<br /> ====================================================================<br /> cookie<br /> ====================================================================<br /> cookie.getCookies()获得所有cookie对象的集合<br /> cookie.getName()获得指定名称的cookie<br /> cookie.getValue()获得cookie对象的值<br /> URLEncoder.encode();将需要保存到cookie中的数据进行编码<br /> URLEncoder.decode();读取cookie信息时将信息解码<br /> ====================================================================<br /> response<br /> ====================================================================<br /> response.addCookie()将一个cookie对象发送到客户端<br /> response.sendRedirect(String path); 重定向<br /> ====================================================================<br /> application<br /> ====================================================================<br /> application.setAttribute(key,value);给application添加属性值<br /> application.getAttribute(key,value);获取指定的值<br /> ====================================================================<br /> session<br /> ====================================================================<br /> session.setMaxInactiveInterval(int num);设置session对象的有效活动时间<br /> session.isNew();判断是否为新用户  返回Boolean<br /> session.setAttribute();<br /> session.getAttribute();<br /> session.invalidate();销毁当前session<br /> ====================================================================
  • mybatis的缓存机制(一级缓存二级缓存和刷新缓存)

    mybatis事物访问数据库的一级缓存和二级缓存和刷新缓存<p>背景:今天在公司的一个项目中查询一个数据,一个query方法,在同一个事务中查询了两次,但是第二次查询的结果始终和第一次完全一致,刚开始以为是查询方法写作了,但是后面一想只有参数不一致,没有什么异同,断点模式追踪发现是mybatis自带的缓存机制导致,顺便记录下。</p> <h2><strong>一、什么是查询缓存</strong></h2> <p>mybatis提供查询缓存,用户减轻数据压力,提供数据库性能,并且提供一级缓存和二级缓存。如下图</p> <p><img alt="" class="img-thumbnail" src="/assist/images/blog/8d7949c5ea3f462f971b5fca1a53a974.png" /><br /> 1、一级缓存是sqlsession级别的缓存,在操作数据库时需要构造sqlsession对象,在对象中有一个(内存区域)数据结构(hashmap)用于存储缓存数据。不同的sqlsession之间的缓存数据区域(hashmap)是互相不影响的。<br /> <br />     一级缓存的作用域是同一个sqlsession,在同一个sqlsession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写入搭配缓存(内存),第二次会从缓存中获取数据,将不在从数据库查询,从而提高查询效率。当一个sqlsession结束后该sqlsession中的一级缓存也就不存在,mybatis默认开启一级缓存。<br /> <br /> 2、二级缓存是mapper级别的缓存,多个sqlsession去操作同一个mapper的sql语句,多个sqlsession区操作数据得到数据会存在二级缓存区域,多个sqlsession可以共用二级缓存,二级缓存是跨sqlsession的。<br /> <br />     二级缓存是多个sqlsession共享的,起作用域是mapper的同以一个namespace,不同的sqlsession两次执行相同的namespace下的sql语句且向sql传递的参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高效率,mybatis默认没有开启二级缓存需要setting全部参数中配置开启二级缓存。<br /> <br /> 如果缓存中有数据就将不会从数据库获取,提高系统性能。</p> <h2> <strong>二、一级缓存</strong><br /> <br /> <img alt="" class="img-thumbnail" src="/assist/images/blog/2b63d8a91f07490cb0e734f90dfe9196.png" /></h2> <p>    第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。如果<strong>sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓</strong>存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。<br />     一级缓存的应用:开发中,是将mybatis和spring进行整合开发,事务控制在service中,一个service方法中包括很多mapper方法调用,一个service中相同的调用mapper中相同方法,后面调取会直接从一级缓存中取数据。方法结束,sqlSession关闭 sqlsession关闭后就销毁数据结构,清空缓存Service结束sqlsession关闭。如果是执行两次service调用查询相同的用户信息,不走一级缓存,因为Service方法结束,sqlSession就关闭,一级缓存就清空。</p> <h2><strong>三、二级缓存</strong></h2> <p>  <img alt="" class="img-thumbnail" src="/assist/images/blog/088bad7be6874e50b281bcaac6f36616.png" /></p> <p>    mybatis二级缓存需要配置开启,二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。数据类型仍然为HashMap。UserMapper有一个二级缓存区域(按namespace分,如果namespace相同则使用同一个相同的二级缓存区),其它mapper也有自己的二级缓存区域(按namespace分)。每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。<br />     开启二级缓存:只需要在mapper.xml文件中添加</p> <pre> <code class="language-xml"><cache/> </code></pre> 如下: <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.cqax.business.dao.PictureMapper"> <!-- 开启mybatis二级缓存 --> <cache/> <resultMap id="BaseResultMap" type="com.cqax.business.model.Picture"> <id column="id" jdbcType="BIGINT" property="id" /> <result column="created" jdbcType="TIMESTAMP" property="created" /> <result column="type" jdbcType="INTEGER" property="type" /> <result column="updated" jdbcType="TIMESTAMP" property="updated" /> <result column="operator_id" jdbcType="BIGINT" property="operatorId" /> <result column="picture_id" jdbcType="BIGINT" property="pictureId" /> <result column="student_id" jdbcType="BIGINT" property="studentId" /> <result column="sign_id" jdbcType="BIGINT" property="signId" /> <result column="signsecretkey" jdbcType="VARCHAR" property="signsecretkey" /> <result column="transferstatus" jdbcType="INTEGER" property="transferstatus" /> <result column="deviceid" jdbcType="VARCHAR" property="deviceid" /> <result column="fingerstatus" jdbcType="TINYINT" property="fingerstatus" /> <result column="sendtimer" jdbcType="INTEGER" property="sendtimer" /> </resultMap></code></pre> <h2><strong>四、mybatis刷新缓存(清空缓存)</strong></h2> <p>在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。 设置statement配置中的flushCache='true' 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。<br /> 比如:</p> <pre> <code class="language-xml"><insertid='insertUser' parameterType='cn.itcast.mybatis.po.User' flushCache='true'></code></pre> 一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存默认情况下为true,我们不用去设置它,这样可以避免数据库脏读。 <h2><strong>五、Mybatis Cache参数</strong></h2> <p>flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。readOnly(只读)属性可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。<br /> 例如:</p> <pre> <code class="language-xml"><cache eviction='FIFO' flushInterval='60000' size='512' readOnly='true'/></code></pre> <p>这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。可用的收回策略有, 默认的是 LRU:</p> <p>1.LRU – 最近最少使用的:移除最长时间不被使用的对象。</p> <p>2.FIFO – 先进先出:按对象进入缓存的顺序来移除它们。</p> <p>3.SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。</p> <p>4.WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。</p> <h2><strong>六、顺带说下mybatis的xml文件sql中每个字段配置属性</strong></h2> <table border="1" cellspacing="0" class="table table-bordered table-hover" style="border-collapse:collapse; border-spacing:0px; border:1px solid #dddddd; box-sizing:border-box; color:#333333; font-family:"Helvetica Neue",Helvetica,Arial,sans-serif; font-size:14px; margin-bottom:20px; max-width:100%; width:1025px"> <tbody> <tr> <td>id</td> <td style="background-color:#f9f9f9; vertical-align:top">在命名空间中唯一的标识符,可以被用来引用这条语句。</td> </tr> <tr> <td>parameterType</td> <td style="vertical-align:top">将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。</td> </tr> <tr> <td>resultType</td> <td style="background-color:#f9f9f9; vertical-align:top">从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。</td> </tr> <tr> <td>resultMap</td> <td style="background-color:#f9f9f9; vertical-align:top">外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同时使用。</td> </tr> <tr> <td>flushCache</td> <td style="background-color:#f9f9f9; vertical-align:top">将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。</td> </tr> <tr> <td>useCache</td> <td style="background-color:#f9f9f9; vertical-align:top">将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。</td> </tr> <tr> <td>timeout</td> <td style="background-color:#f9f9f9; vertical-align:top">这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。</td> </tr> <tr> <td>fetchSize</td> <td style="background-color:#f9f9f9; vertical-align:top">这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为 unset(依赖驱动)。</td> </tr> <tr> <td>statementType</td> <td style="background-color:#f9f9f9; vertical-align:top">STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。</td> </tr> <tr> <td>resultSetType</td> <td style="background-color:#f9f9f9; vertical-align:top">FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认值为 unset (依赖驱动)。</td> </tr> <tr> <td>databaseId</td> <td style="background-color:#f9f9f9; vertical-align:top">如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。</td> </tr> <tr> <td>resultOrdered</td> <td>这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。</td> </tr> <tr> <td>resultSets</td> <td style="background-color:#f9f9f9; vertical-align:top">这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的。</td> </tr> </tbody> </table> <h3>下面特别说明下之前遇到的问题</h3> 1.flushCache<br /> 当为select语句时:flushCache默认为false,表示任何时候语句被调用,都不会去清空本地缓存和二级缓存。<br /> 当为insert、update、delete语句时:flushCache默认为true,表示任何时候语句被调用,都会导致本地缓存和二级缓存被清空。<br /> <br /> 2.useCache<br /> 当为select语句时:useCache默认为true,表示会将本条语句的结果进行二级缓存。<br /> 当为insert、update、delete语句时:useCache属性在该情况下没有。<br /> <br /> 当为select语句的时候,如果没有去配置flushCache、useCache,那么默认是启用缓存的,所以,如果有必要,那么就需要人工修改配置,修改结果类似下面: <pre> <code class="language-xml"><select id="save" parameterType="XX" flushCache="true" useCache="false">     …… </select></code></pre> update 的时候如果 flushCache="false",则当你更新后,查询的数据数据还是老的数据。<br /> <br /> <br />  
  • JONSBO 乔思伯 C2 ITX 机箱散热完美解决方案

    JONSBO 乔思伯 C2 ITX 机箱散热完美解决方案<h2>1.问题描述:</h2> 大家都知道JONSBO的C2机箱比较小巧好看.但是,事物都有两面性好看的背后隐藏着散热的问题,本博客主要讲解如何搭配出一套尽量散热靠谱的小钢炮主机。 <h2>2.硬件的选择</h2> <br /> 2.1CPU选择<br /> CPU选择方面,由于空间小散热问题优先考虑Intel的CPU,i5不带K的以下,推荐I3/I5或者最新的奔腾G4560<br /> <br /> 2.2CPU散热器选择<br /> 由于机箱的限制散热器高度也被限制。塔式已经默认不能用。所以在下压吹风的里面选择。如果选择I5的话推荐使用JONSBO的HP400或者类似的4根铜管的散热器,I3及以下则原装风扇即可<br /> <br /> 2.3电源的选择<br /> 电源的选择很重要。我们需要选择一块非常规的12CM的侧吹的电源.普通ATX电源的风扇如下:<br /> <img alt="普通电源风扇布局" class="img-thumbnail" src="/assist/images/blog/54032e9657c548faa07166576e339531.png" /><br /> 很明显这样的电源风扇会和CPU抢入风导致两边都热。所以我们需要选择下面这种类型的电源:<br /> <img alt="电源推荐" class="img-thumbnail" src="/assist/images/blog/9d824dd38aa14bd8a56d404f34ffc2b5.png" /><br /> 但是这种电源有个缺点就是功率一般偏小,300W左右.所以再显卡选择方面要适当放弃一些。<br /> <br /> 2.4显卡选择<br /> 推荐英伟达的750Ti/1050/1050TI<br />  
  • Python教程-Python保留字符/关键词

    ​Python关键字是python编程语言的保留字,这些关键字不能用于其他目的​Python关键字是python编程语言的保留字,这些关键字不能用于其他目的。Python中有35个关键字-下面列出了它们的用法关键词描述and一个逻辑的“并且”操作,如果所有条件都是True,则返回Truex = (5 > 3 and 5 < 10)print(x)    # Trueor一个逻辑的“或者”操作,如果其中一个条件是true则返回True,如果全部条件都是false,则返回Falsex = (5 > 3 or 5 > 10)print(x)    # Trueas用来创建一个别名import calendar as cprint(c.month_name[1])  #Januaryassert它可以用于调试代码。 它会测试一个条件并返回True,否则返回True。x = "hello" assert x == "goodbye", "x should be 'hello'"  # AssertionErrorasync它被用来声明一个作为协程的函数,就像@ asyncio.coroutine装饰器所做的一样。async def ping_server(ip):await它用于调用异步协程。async def ping_local():    return await ping_server('192.168.1.1')class用于创建一个类class User:  name = "John"  age = 36def它用于创建或定义函数/方法。def my_function():  print("Hello world !!") my_function()del它用于删除对象。 在Python中,所有事物都是对象,因此del关键字也可用于删除变量,列表或列表的一部分等。x = "hello" del xif它用于创建条件语句,该条件语句仅在条件为True时才允许我们执行代码块。x = 5 if x > 3:  print("it is true")elif他是 else if的缩写i = 5 if i > 0:    print("Positive")elif i == 0:    print("ZERO")else:    print("Negative")else它在if..else语句中确定条件为False时该如何处理。i = 5 if i > 0:    print("Positive")else:    print("Negative")它也可以用在try ...区块中。x = 5 try:    x > 10except:    print("Something went wrong")else:    print("Normally execute the code")try它定义了一个代码块来测试它是否包含任何错误。except如果try块引发错误,它将定义要运行的代码块。try:    x > 3except:    print("Something went wrong")finally它定义了一个代码块,无论try块是否引发错误,该代码块都将执行。try:    x > 3except:    print("Something went wrong")finally:     print("I will always get executed")raise它用于手动引发异常。x = "hello" if not type(x) is int:    raise TypeError("Only integers are allowed")False它是一个布尔值,与0相同。True它是一个布尔值,与1相同。for它用于创建for循环。 for循环可用于遍历序列(如列表,元组等)。for x in range(1, 9):    print(x)while它用于创建while循环。 循环继续进行,直到条件语句为假。x = 0 while x < 9:    print(x)    x = x + 1break它用于中断for循环或while循环。i = 1 while i < 9:    print(i)    if i == 3:        break    i += 1continue它用于在for循环(或while循环)中结束当前迭代,并继续进行下一个迭代。for i in range(9):    if i == 3:        continue    print(i)import它用于导入模块。import datetimefrom它仅用于从模块中导入指定的节。from datetime import timeglobal它用于从非全局范围创建全局变量,例如 在函数内部。def myfunction():    global x    x = "hello"in1.用于检查序列(列表,范围,字符串等)中是否存在值。2.它也用于迭代for循环中的序列。fruits = ["apple", "banana", "cherry"] if "banana" in fruits:    print("yes") for x in fruits:    print(x)is它用于测试两个变量是否引用同一对象。a = ["apple", "banana", "cherry"]b = ["apple", "banana", "cherry"]c = a print(a is b)   # Falseprint(a is c)   # Truelambda它用于创建小的匿名函数。 它们可以接受任意数量的参数,但只能有一个表达式。x = lambda a, b, c : a + b + c print(x(5, 6, 2))None它用于定义一个空值或完全没有值。 None与0,False或空字符串不同。None是它自己的数据类型(NoneType),并且只有None可以是None。x = None if x:  print("Do you think None is True")else:  print("None is not True...")      # Prints this statementnonlocal它用于声明变量不是局部变量。 它用于在嵌套函数内部使用变量,其中变量不应属于内部函数。def myfunc1():    x = "John"    def myfunc2():        nonlocal x        x = "hello"    myfunc2()    return x print(myfunc1())not它是一个逻辑运算符,并反转True或False的值。x = False print(not x)    # Truepass它用作将来代码的占位符。 当执行pass语句时,什么也不会发生,但是当不允许使用空代码时,可以避免出现错误。循环,函数定义,类定义或if语句中不允许使用空代码。for x in [0, 1, 2]:            passreturn它是退出一个函数并返回一个值。def myfunction():            return 3+3with用于简化异常处理yield要结束一个函数,返回一个生成器
  • spring boot整合mybaties

    spring boot框架整合mybaties数据库暂时选用MySQL<br /> 本博文主要讲解spring boot项目整合mybaties<br /> 1.最终项目结构图<br /> <img alt="结构" class="img-thumbnail" src="/assist/images/blog/e0b0519f-a4be-43fe-aabe-d57927ba82b5.jpg" style="height:564px; width:305px" /><br /> 2.文件清单<br /> 清单:pom.xml <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>demo-springboot-mybaties</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demo-springboot-mybaties</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.1.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <!-- mybaties --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.2.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> </code></pre> 清单:User.java <pre> <code class="language-java">package com.example.pojo; public class User { private Long id; private String userName; private String userSex; private int userAge; public User() { } public User(String userName, String userSex, int userAge) { super(); this.userName = userName; this.userSex = userSex; this.userAge = userAge; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserSex() { return userSex; } public void setUserSex(String userSex) { this.userSex = userSex; } public int getUserAge() { return userAge; } public void setUserAge(int userAge) { this.userAge = userAge; } } </code></pre> 清单:UserMapper.java <pre> <code class="language-java">package com.example.mapper; import java.util.List; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Result; import org.apache.ibatis.annotations.Results; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import com.example.pojo.User; public interface UserMapper { /** * 查询所有用户信息 * * @return */ @Select("select * from user") @Results(value = { @Result(property = "userSex", column = "user_sex", javaType = String.class), @Result(property = "userName", column = "user_name"), @Result(property = "userAge", column = "user_age") }) List<User> findList(); /** * 通过ID查询 * * @param id * @return */ @Select("select * from user u where u.id=#{id}") User findOne(@Param("id") Long id); /** * 新增一个 * * @param user */ @Insert("insert into user (user_name,user_sex,user_age) values(#{userName},#{userSex},#{userAge})") void insert(User user); /** * 修改 * * @param user */ @Update("update user u set u.user_name=#{userName},u.user_sex=#{userSex},u.user_age=#{userAge} where u.id=#{id}") void update(User user); /** * 删除 * * @param id */ @Delete("delete from user where id=#{id}") void delete(@Param("id") Long id); } </code></pre> 清单:UserController.java <pre> <code class="language-java">package com.example.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.example.mapper.UserMapper; import com.example.pojo.User; @RestController public class UserController { @Autowired UserMapper userMapper; /** * index * * @return */ @RequestMapping("/") public String index() { return "User Info By Mybaties"; } @RequestMapping("/user/list.json") public Object allUsers() { List<User> users = userMapper.findList(); return users; } } </code></pre> 清单:Application.java <pre> <code class="language-java">package com.example; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBootApplication @MapperScan(basePackages = { "com.example.mapper" }) // 自动扫描mapper @EnableTransactionManagement//启用事物管理,在service上使用@Transactional(注意是spring的 注解) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } </code></pre> 清单:application.properties <pre> <code class="language-perl">#==================DataSource Config Start================== #name #spring.datasource.name=test #url #spring.datasource.url=jdbc:sqlserver://192.168.xxx.xxx;instanceName=sql_03;DatabaseName=edu;integratedSecurity=false spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8 #DriverClass #spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver spring.datasource.driver-class-name=com.mysql.jdbc.Driver #DB username spring.datasource.username=root #DB password spring.datasource.password=root #==================DataSource Config End================== #==================mybaties Config Start================== #ORM Bean Package mybatis.type-aliases-package=com.example.pojo mybatis.mapper-locations=classpath:/mapper/*.xml #打印mybatiesSql语句 logging.level.com.example.mapper=DEBUG #==================mybaties Config End ================== </code></pre> 清单:UserMapperTest.java <pre> <code class="language-java">package com.example.test; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.example.mapper.UserMapper; import com.example.pojo.User; @RunWith(SpringRunner.class) @SpringBootTest public class UserMapperTest { @Autowired private UserMapper userMapper; // @Test public void testInsert() { try { userMapper.insert(new User("xqlee1", "男", 26)); userMapper.insert(new User("xqlee2", "男", 23)); userMapper.insert(new User("xqlee3", "男", 27)); } catch (Exception e) { e.printStackTrace(); } } // @Test public void testUpdate() { try { User user = new User("测试0000", "男", 23); user.setId(1l); userMapper.update(user); } catch (Exception e) { e.printStackTrace(); } } //@Test public void testQuery() { try { List<User> users=userMapper.findList(); for(User u:users){ System.out.println("ID:"+u.getId()+" Name:"+u.getUserName()+" Sex:"+u.getUserSex()+" Age:"+u.getUserAge()); } } catch (Exception e) { e.printStackTrace(); } } @Test public void testDelete(){ try { userMapper.delete(1l); testQuery(); } catch (Exception e) { e.printStackTrace(); } } } </code></pre>