精华内容
下载资源
问答
  • 如果对一个类或者集合配置了延迟检索策略,那么必须当代理类实例或代理集合处于持久化状态(即处于Session范围内)时,才能初始化它。如果在游离状态时才初始化它,就会产生延迟初始化错误。下面把Customer.hbm.xml...
     
    

    延迟初始化错误是运用Hibernate开发项目时最常见的错误。如果对一个类或者集合配置了延迟检索策略,那么必须当代理类实例或代理集合处于持久化状态(即处于Session范围内)时,才能初始化它。如果在游离状态时才初始化它,就会产生延迟初始化错误。

    下面把Customer.hbm.xml文件的<class>元素的lazy属性设为true,表示使用延迟检索策略:

    <class name="mypack.Customer" table="CUSTOMERS" lazy="true">

    当执行Session的load()方法时,Hibernate不会立即执行查询CUSTOMERS表的select语句,仅仅返回Customer类的代理类的实例,这个代理类具由以下特征:

    (1) 由Hibernate在运行时动态生成,它扩展了Customer类,因此它继承了Customer类的所有属性和方法,但它的实现对于应用程序是透明的。
    (2) 当Hibernate创建Customer代理类实例时,仅仅初始化了它的OID属性,其他属性都为null,因此这个代理类实例占用的内存很少。
    (3) 当应用程序第一次访问Customer代理类实例时(例如调用customer.getXXX()或customer.setXXX()方法), Hibernate会初始化代理类实例,在初始化过程中执行select语句,真正从数据库中加载Customer对象的所有数据。但有个例外,那就是当 应用程序访问Customer代理类实例的getId()方法时,Hibernate不会初始化代理类实例,因为在创建代理类实例时OID就存在了,不必 到数据库中去查询。

    提示:Hibernate采用CGLIB工具来生成持久化类的代理类。CGLIB是一个功能强大的Java字节码生成工具,它能够在程序运行时动态生成扩展 Java类或者实现Java接口的代理类。

    以下代码先通过Session的load()方法加载Customer对象,然后访问它的name属性:

    tx = session.beginTransaction();
    Customer customer=(Customer)session.load(Customer.class,new Long(1));
    customer.getName();
    tx.commit();

    在运行session.load()方法时Hibernate不执行任何select语句,仅仅返回Customer类的代理类的实例,它的OID 为1,这是由load()方法的第二个参数指定的。当应用程序调用customer.getName()方法时,Hibernate会初始化 Customer代理类实例,从数据库中加载Customer对象的数据,执行以下select语句:

    select * from CUSTOMERS where ID=1;
    select * from ORDERS where CUSTOMER_ID=1;

    当<class>元素的lazy属性为true,会影响Session的load()方法的各种运行时行为,下面举例说明。

    1.如果加载的Customer对象在数据库中不存在,Session的load()方法不会抛出异常,只有当运行customer.getName()方法时才会抛出以下异常:

    ERROR LazyInitializer:63 - Exception initializing proxy
    net.sf.hibernate.ObjectNotFoundException: No row with the given identifier exists: 1, of class:
    mypack.Customer

    2.如果在整个Session范围内,应用程序没有访问过Customer对象,那么Customer代理类的实例一直不会被初始化,Hibernate不会执行任何select语句。以下代码试图在关闭Session后访问Customer游离对象:

    tx = session.beginTransaction();
    Customer customer=(Customer)session.load(Customer.class,new Long(1));
    tx.commit();
    session.close();
    customer.getName();

    由于引用变量customer引用的Customer代理类的实例在Session范围内始终没有被初始化,因此在执行customer.getName()方法时,Hibernate会抛出以下异常:

    ERROR LazyInitializer:63 - Exception initializing proxy
    net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed

    由此可见,Customer代理类的实例只有在当前Session范围内才能被初始化。

    3.net.sf.hibernate.Hibernate类的initialize()静态方法用于在Session范围内显式初始化代理类实例,isInitialized()方法用于判断代理类实例是否已经被初始化。例如:

    tx = session.beginTransaction();
    Customer customer=(Customer)session.load(Customer.class,new Long(1));
    if(!Hibernate.isInitialized(customer))
    Hibernate.initialize(customer);
    tx.commit();
    session.close();
    customer.getName();

    以上代码在Session范围内通过Hibernate类的initialize()方法显式初始化了Customer代理类实例,因此当Session关闭后,可以正常访问Customer游离对象。

    4.当应用程序访问代理类实例的getId()方法时,不会触发Hibernate初始化代理类实例的行为,例如:

    tx = session.beginTransaction();
    Customer customer=(Customer)session.load(Customer.class,new Long(1));
    customer.getId();
    tx.commit();
    session.close();
    customer.getName();

    当应用程序访问customer.getId()方法时,该方法直接返回Customer代理类实例的OID值,无需查询数据库。由于引用变量 customer始终引用的是没有被初始化的Customer代理类实例,因此当Session关闭后再执行customer.getName()方法, Hibernate会抛出以下异常:

    ERROR LazyInitializer:63 - Exception initializing proxy
    net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed


    解决方法:

    由于hibernate采用了lazy=true,这样当你用hibernate查询时,返回实际为利用cglib增强的代理类,但其并没有实际填 充;当你在前端,利用它来取值(getXXX)时,这时Hibernate才会到数据库执行查询,并填充对象,但此时如果和这个代理类相关的 session已关闭掉,就会产生种错误.
    在做一对多时,有时会出现"could not initialize proxy - clothe owning Session was sed,这个好像是hibernate的缓存问题.问题解决:需要在<many-to-one>里设置lazy="false". 但有可能会引发另一个异常叫

    failed to lazily initialize a collection of role: XXXXXXXX, no session or session was closed

    ?
    解决方法:在web.xml中加入
    <filter>
        <filter-name>hibernateFilter</filter-name>
        <filter-class>
         org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
        </filter-class>
    </filter
    <filter-mapping>
        <filter-name>hibernateFilter</filter-name>
        <url-pattern>*.do</url-pattern>
    </filter-mapping>
    就可以了;

    参考了:
    Hibernate与延迟加载:

    Hibernate对象关系映射提供延迟的与非延迟的对象初始化。非延迟加载在读取一个对象的时候会将与这个对象所有相关的其他对象一起读取出来。 这有时会导致成百的(如果不是成千的话)select语句在读取对象的时候执行。这个问题有时出现在使用双向关系的时候,经常会导致整个数据库都在初始化 的阶段被读出来了。当然,你可以不厌其烦地检查每一个对象与其他对象的关系,并把那些最昂贵的删除,但是到最后,我们可能会因此失去了本想在ORM工具中 获得的便利。


    一个明显的解决方法是使用Hibernate提供的延迟加载机制。这种初始化策略只在一个对象调用它的一对多或多对多关系时才将关系对象读 取出来。这个过程对开发者来说是透明的,而且只进行了很少的数据库操作请求,因此会得到比较明显的性能提升。这项技术的一个缺陷是延迟加载技术要求一个 Hibernate会话要在对象使用的时候一直开着。这会成为通过使用DAO模式将持久层抽象出来时的一个主要问题。为了将持久化机制完全地抽象出来,所 有的数据库逻辑,包括打开或关闭会话,都不能在应用层出现。最常见的是,一些实现了简单接口的DAO实现类将数据库逻辑完全封装起来了。一种快速但是笨拙 的解决方法是放弃DAO模式,将数据库连接逻辑加到应用层中来。这可能对一些小的应用程序有效,但是在大的系统中,这是一个严重的设计缺陷,妨碍了系统的 可扩展性。

    在Web层进行延迟加载

    幸运的是,Spring框架为Hibernate延迟加载与DAO模式的整合提供了一种方便的解决方法。对那些不熟悉Spring与 Hibernate集成使用的人,我不会在这里讨论过多的细节,但是我建议你去了解Hibernate与Spring集成的数据访问。以一个Web应用为 例,Spring提供了OpenSessionInViewFilter和OpenSessionInViewInterceptor。我们可以随意选择 一个类来实现相同的功能。两种方法唯一的不同就在于interceptor在Spring容器中运行并被配置在web应用的上下文中,而Filter在 Spring之前运行并被配置在web.xml中。不管用哪个,他们都在请求将当前会话与当前(数据库)线程绑定时打开Hibernate会话。一旦已绑 定到线程,这个打开了的Hibernate会话可以在DAO实现类中透明地使用。这个会话会为延迟加载数据库中值对象的视图保持打开状态。一旦这个逻辑视 图完成了,Hibernate会话会在Filter的doFilter方法或者Interceptor的postHandle方法中被关闭。下面是每个组 件的配置示例:

     

    Interceptor的配置:


    <beans>
    <bean id="urlMapping"
    class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="interceptors">
    <list>
    <ref bean="openSessionInViewInterceptor"/>
    </list>
    </property>
    <property name="mappings">

    </bean>

    <bean name="openSessionInViewInterceptor"
    class="org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor">
    <property name="sessionFactory"><ref bean="sessionFactory"/></property>
    </bean>
    </beans>

    Filter的配置


    <web-app>

    <filter>
    <filter-name>hibernateFilter</filter-name>
    <filter-class>
    org.springframework.orm.hibernate.support.OpenSessionInViewFilter
    </filter-class>
    </filter>

    <filter-mapping>
    <filter-name>hibernateFilter</filter-name>
    <url-pattern>*. spring </url-pattern>
    </filter-mapping>

    </web-app>


    实现Hibernate的Dao接口来使用打开的会话是很容易的。事实上,如果你已经使用了Spring框架来实现你的Hibernate Dao,很可能你不需要改变任何东西。方便的HibernateTemplate公用组件使访问数据库变成小菜一碟,而DAO接口只有通过这个组件才可以 访问到数据库。下面是一个示例的DAO:


    public class HibernateProductDAO extends HibernateDaoSupport implements ProductDAO {

    public Product getProduct(Integer productId) {
    return (Product)getHibernateTemplate().load(Product.class, productId);
    }

    public Integer saveProduct(Product product) {
    return (Integer) getHibernateTemplate().save(product);
    }

    public void updateProduct(Product product) {
    getHibernateTemplate().update(product);
    }
    }


    在业务逻辑层中使用延迟加载

    即使在视图外面,Spring框架也通过使用AOP 拦截器 HibernateInterceptor来使得延迟加载变得很容易实现。这个Hibernate 拦截器透明地将调用配置在Spring应用程序上下文中的业务对象中方法的请求拦截下来,在调用方法之前打开一个Hibernate会话,然后在方法执行 完之后将会话关闭。让我们来看一个简单的例子,假设我们有一个接口BussinessObject:


    public     interface    BusinessObject     {
    public     void    doSomethingThatInvolvesDaos();
    }
    类BusinessObjectImpl实现了BusinessObject接口:

    public     class    BusinessObjectImpl    implements    BusinessObject     {
    public     void    doSomethingThatInvolvesDaos()     {
    //    lots of logic that calls
    //    DAO classes Which access
    //    data objects lazily 

     

    通过在Spring应用程序上下文中的一些配置,我们可以让将调用BusinessObject的方法拦截下来,再令它的方法支持延迟加载。看看下面的一个程序片段:

     

    <beans>
    <bean id="hibernateInterceptor" class="org.springframework.orm.hibernate.HibernateInterceptor">
    <property name="sessionFactory">
    <ref bean="sessionFactory"/>
    </property>
    </bean>
    <bean id="businessObjectTarget" class="com.acompany.BusinessObjectImpl">
    <property name="someDAO"><ref bean="someDAO"/></property>
    </bean>
    <bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target"><ref bean="businessObjectTarget"/></property>
    <property name="proxyInterfaces">
    <value>com.acompany.BusinessObject</value>
    </property>
    <property name="interceptorNames">
    <list>
    <value>hibernateInterceptor</value>
    </list>
    </property>
    </bean>
    </beans>

    当businessObject被调用的时候,HibernateInterceptor打开一个Hibernate会话,并将调用请求传递给 BusinessObjectImpl对象。当BusinessObjectImpl执行完成后,HibernateInterceptor透明地关闭了 会话。应用层的代码不用了解任何持久层逻辑,还是实现了延迟加载。


    在单元测试中测试延迟加载

    最后,我们需要用J-Unit来测试我们的延迟加载程序。我们可以轻易地通过重写TestCase类中的setUp和tearDown方法来实现这个要求。我比较喜欢用这个方便的抽象类作为我所有测试类的基类。


    public abstract class MyLazyTestCase extends TestCase {

    private SessionFactory sessionFactory;
    private Session session;

    public void setUp() throws Exception {
    super.setUp();
    SessionFactory sessionFactory = (SessionFactory) getBean("sessionFactory");
    session = SessionFactoryUtils.getSession(sessionFactory, true);
    Session s = sessionFactory.openSession();
    TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s));

    }

    protected Object getBean(String beanName) {
    //Code to get objects from Spring application context
    }

    public void tearDown() throws Exception {
    super.tearDown();
    SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    Session s = holder.getSession();
    s.flush();
    TransactionSynchronizationManager.unbindResource(sessionFactory);
    SessionFactoryUtils.closeSessionIfNecessary(s, sessionFactory);
    }

    展开全文
  • 曾经测试对一个包含400多万条记录(有索引)的表执行一条条件查询,其查询时间竟然高达40几秒,相信这么高的查询延时,任何用户都会抓狂。因此如何提高sql语句查询效率,显得十分重要。 1、对查询进行优化,应...

    由于在参与的实际项目中发现当mysql表的数据量达到百万级时,普通SQL查询效率呈直线下降,而且如果where中的查询条件较多时,其查询速度简直无法容忍。曾经测试对一个包含400多万条记录(有索引)的表执行一条条件查询,其查询时间竟然高达40几秒,相信这么高的查询延时,任何用户都会抓狂。因此如何提高sql语句查询效率,显得十分重要。


    1、对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。


    2、应尽量避免在 where 子句中使用!=或<>操作符,否则引擎放弃使用索引而进行全表扫描。


    3、应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:

    select id from t where num is null

    可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:

    select id from t where num=0


    4、尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:

    select id from t where num=10 or num=20

    可以这样查询:

    select id from t where num=10
    union all
    select id from t where num=20


    5、下面的查询也将导致全表扫描:(不能前置百分号)

    select id from t where name like ‘%c%’
    若要提高效率,可以考虑全文检索。


    6、in 和 not in 也要慎用,否则会导致全表扫描,如:

    select id from t where num in(1,2,3)
    对于连续的数值,能用 between 就不要用 in 了:
    select id from t where num between 1 and 3


    7、如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:

    select id from t where num=@num

    可以改为强制查询使用索引:

    select id from t with(index(索引名)) where num=@num


    8、应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:

    select id from t where num/2=100

    应改为:
    select id from t where num=100*2


    9、应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:

    select id from t where substring(name,1,3)=’abc’–name以abc开头的id
    select id from t where datediff(day,createdate,’2005-11-30′)=0–’2005-11-30′生成的id

    应改为:

    select id from t where name like ‘abc%’
    select id from t where createdate>=’2005-11-30′ and createdate<’2005-12-1′


    10、不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。


    11、在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使 用,并且应尽可能的让字段顺序与索引顺序相一致。


    12、不要写一些没有意义的查询,如需要生成一个空表结构:

    select col1,col2 into #t from t where 1=0
    这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
    create table #t(…)


    13、很多时候用 exists 代替 in 是一个好的选择:

    select num from a where num in(select num from b)

    用下面的语句替换:
    select num from a where exists(select 1 from b where num=a.num)


    14、并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段 sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。


    15、索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要。


    16.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。



    17、尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会 逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。


    18、尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。


    19、任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。


    20、尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。


    21、避免频繁创建和删除临时表,以减少系统表资源的消耗。


    22、临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使 用导出表。



    23、在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。



    24、如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。


    25、尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。


    26、使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。


    27、与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时 间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。


    28、在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。


    29、尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。


    30、尽量避免大事务操作,提高系统并发能力。


    展开全文
  • RocketMQ-延时消息Demo及实现原理分析

    万次阅读 多人点赞 2019-05-26 19:34:18
    文章目录延时消息Producer Demo源码分析延时消息持久化内部变量含义初始化load方法start方法延时消息调度总结 假设有这么一个需求,用户下单后如果30分钟未支付,则该订单需要被关闭。你会怎么做? 最简单的做法,...


    假设有这么一个需求,用户下单后如果30分钟未支付,则该订单需要被关闭。你会怎么做?

    最简单的做法,可以服务端启动个定时器,隔个几秒扫描数据库中待支付的订单,如果(当前时间-订单创建时间)>30分钟,则关闭订单。

    这种方案优点是实现简单,缺点呢?

    定时扫描意味着隔个几秒就得查一次数据库,频率高的情况下,如果数据库中订单总量特别大,这种高频扫描会对数据库带来一定压力,待付款订单特别多时(做个爆品秒杀活动,或者啥促销活动),若一次性查到内存中,容易引起宕机,需要分页查询,多少也会有一定数据库层面压力

    那么有没其他解决方案?关键有2点设计要求

    1. 能够在指定时间间隔后触发某个业务操作
    2. 能够应对业务数据量特别大的特殊场景

    RocketMQ延时消息能够完美的解决上述需求,正常的消息在投递后会立马被消费者所消费,而延时消息在投递时,需要设置指定的延时级别(不同延迟级别对应不同延迟时间),即等到特定的时间间隔后消息才会被消费者消费,这样就将数据库层面的压力转移到了MQ中,也不需要手写定时器,降低了业务复杂度,同时MQ自带削峰功能,能够很好的应对业务高峰

    下面先从Demo入手,开始分析延时消息使用及原理

    延时消息Producer Demo

    延时消息的关键点在于Producer生产者需要给消息设置特定延时级别,消费端代码与正常消费者没有差别。

    public class Producer {
        public static void main(String[] args) throws MQClientException, InterruptedException {
    
            DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
            //设置namesrv地址
            producer.setNamesrvAddr("111.231.110.149:9876");
            //启动生产者
            producer.start();
    
            //发送10条消息
            for (int i = 0; i < 10; i++) {
                try {
                    Message msg = new Message("TopicTest" /* Topic */,
                        "TagA" /* Tag */,
                        ("test message" + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
                    );
                    //设置消息延时级别  3对应10秒后发送
                    msg.setDelayTimeLevel(3);
    
                    SendResult sendResult = producer.send(msg);
    
                    System.out.printf("%s%n", sendResult);
                } catch (Exception e) {
                    e.printStackTrace();
                    Thread.sleep(1000);
                }
            }
    
            /*
             * Shut down once the producer instance is not longer in use.
             */
            producer.shutdown();
        }
    }
    

    设置消息延时级别的方法是setDelayTimeLevel(),目前RocketMQ不支持任意时间间隔的延时消息,只支持特定级别的延时消息,什么意思呢?

    看下MQ中默认延时级别配置,延时级别配置代码在MessageStoreConfig#messageDelayLevel中

    private String messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";
    

    延时级别1对应延时1秒后发送消息
    延时级别2对应延时5秒后发送消息
    延时级别3对应延时10秒后发送消息
    以此类推。。

    源码分析

    先从延时消息延迟级别设置与broker端消息持久化入手

    延时消息持久化

    通过上面Demo知道,延时消息在发送时,设置了delayLevel,两个问题

    1. 延迟级别设置后与普通消息有什么区别呢?
    2. broker接收到一个设置了延迟级别的消息后,又做了哪些特殊处理呢?

    先看下Message#setDelayTimeLevel方法代码,可以看到延迟级别设置后,消息体的属性里多了一个多了一个PROPERTY_DELAY_TIME_LEVEL的属性,其值为“Delay”,value为延迟级别

    public void setDelayTimeLevel(int level) {
            this.putProperty(MessageConst.PROPERTY_DELAY_TIME_LEVEL, String.valueOf(level));
        }
    
    public static final String PROPERTY_DELAY_TIME_LEVEL = "DELAY";
    

    再看下这个延迟属性哪里被引用了,即看下Message#getDelayTimeLevel方法在哪被引用了,可以看到在消息存储时,CommitLog中有引用
    在这里插入图片描述

    看下CommitLog中的相关代码
    在这里插入图片描述
    延时消息其核心就是两点

    1. 替换消息的topic为特定延时消息topic,queueId为delayLevel-1
    2. 备份消息原有的topic,queueId,方便后面重新取出进行消息投递

    CommitLog中还有一个关键点在checkMessageAndReturnSize中
    在这里插入图片描述
    延时消息的tagsCode存储的的是消息应该被投递的时间,具体后面消息调度发送会介绍

    计算消息投递时间的方法computeDeliverTimestamp如下,delayLevelTable是一个key为延迟级别,value为延迟级别对应的延迟时间(毫秒)的一个map,即 消息投递时间 = 消息存储时间+延迟时间

    public long computeDeliverTimestamp(final int delayLevel, final long storeTimestamp) {
            Long time = this.delayLevelTable.get(delayLevel);
            if (time != null) {
                return time + storeTimestamp;
            }
    
            return storeTimestamp + 1000;
        }
    

    内部变量含义

    延时消息定时投递相关具体实现代码在ScheduleMessageService中,先看下变量定义

    变量名 含义
    String SCHEDULE_TOPIC = “SCHEDULE_TOPIC_XXXX” 定时消息的特定topic
    long FIRST_DELAY_TIME = 1000L 第一次调度的时间间隔
    long DELAY_FOR_A_WHILE = 100L 每次延时消息调度之间的时间间隔
    long DELAY_FOR_A_PERIOD = 10000L 延时消息发送失败后,再次调度的时间间隔
    ConcurrentMap<Integer /* level /, Long/ delay timeMillis */> delayLevelTable 延时级别做key,延时时间(毫秒)做value的map,方便从延时级别找到对应延时时间
    ConcurrentMap<Integer /* level /, Long/ offset */> offsetTable 延时级别做key,消费进度做value的map,方便找到每个延时级别对应队列的消费进度

    初始化

    DefaultMessageStore在启动时,会调用ScheduleMessageService#load()方法来加载消息消费进度和初始化延迟级别对应map,然后调用ScheduleMessageService#start()方法来启动类

    load方法

    public boolean load() {
            boolean result = super.load();
            result = result && this.parseDelayLevel();
            return result;
        }
    

    ScheduleMessageService继承自ConfigManager类,super.load()方法对应

    public boolean load() {
            String fileName = null;
            try {
                fileName = this.configFilePath();
                String jsonString = MixAll.file2String(fileName);
    
                if (null == jsonString || jsonString.length() == 0) {
                    return this.loadBak();
                } else {
                    this.decode(jsonString);
                    log.info("load " + fileName + " OK");
                    return true;
                }
            } catch (Exception e) {
                log.error("load " + fileName + " failed, and try to load backup file", e);
                return this.loadBak();
            }
        }
    

    分为两步

    1. super.load()方法

    一、先从指定路径configFilePath方法获取消息进度持久化存储路径,ScheduleMessageService中延时消息持久化路径存储路径代码在StorePathConfigHelper#getDelayOffsetStorePath中,
    对应到物理机中路径就是${user.home}/store/config/delayOffset.json
    内容如下,key为延时级别,value为消费进度
    在这里插入图片描述

    再调用decode方法将delayOffset.json转换成成map,即前面提到的offsetTable

    1. parseDelayLevel方法
      解析MessageStoreConfig中messageDelayLevel的定义,并转换成delayLevelTable

    综上所述, load方法完成延时消息消费进度加载delayLevel的加载

    start方法

    代码如下

    在这里插入图片描述

    步骤比较清晰

    1. 遍历延时级别map,为每个延时级别创建一个DeliverDelayedMessageTimerTask定时任务,并设置指定offset,第一次调度延时为FIRST_DELAY_TIME,即1s
    2. 每隔10秒持久化延时消息消费进度

    延时消息调度

    延时消息定时器调度核心,在于DeliverDelayedMessageTimerTask的任务执行方法executeOnTimeup中,先看下整体结构,代码较长,步骤说明直接写在代码Todo中

    在这里插入图片描述
    其中根据,delayLevel获取消费队列id的方法如下,即queueId = delayLevel-1

    public static int delayLevel2QueueId(final int delayLevel) {
            return delayLevel - 1;
        }
    

    再具体看下找到延迟级别对应消费队列,并且从offset获取到指定消费位点消息的情况,即if (bufferCQ != null)的代码块中的内容
    在这里插入图片描述
    核心逻辑就是取出tagCode(延时消息持久化时,tagsCode存储的是消息投递时间),解析成消息投递时间,与当前时间戳做差,判断是否应该进行消息投递,具体进行消息投递的方法,在if (countdown <= 0)中,看下代码

    在这里插入图片描述
    重新构建投递消息的关键点在于messageTimeup中,其构建了一个新的消息,并从延时消息属性中恢复出了原有的topic,queueId,再调用putMessage重新进行投递
    在这里插入图片描述

    总结

    基本思路已经介绍完,梳理下延时消息实现思路

    1. producer端设置消息delayLevel延迟级别,消息属性DELAY中存储了对应了延时级别
    2. broker端收到消息后,判断延时消息延迟级别,如果大于0,则备份消息原始topic,queueId,并将消息topic改为延时消息队列特定topic(SCHEDULE_TOPIC),queueId改为延时级别-1
    3. mq服务端ScheduleMessageService中,为每一个延迟级别单独设置一个定时器,定时(每隔1秒)拉取对应延迟级别的消费队列
    4. 根据消费偏移量offset从commitLog中解析出对应消息
    5. 从消息tagsCode中解析出消息应当被投递的时间,与当前时间做比较,判断是否应该进行投递
    6. 若到达了投递时间,则构建一个新的消息,并从消息属性中恢复出原始的topic,queueId,并清除消息延迟属性,从新进行消息投递
    展开全文
  • 用nfs作为StorageClass的后端存储,然后创建pvc挂载到每个微服务的pod中来做日志的集中化和持久化管理。 在使用的过程中发现在nfs服务的源目录中用 tailf 查看日志会有很大的延迟,延迟可以达到几十秒。 ...
  • 框架封装了很多的细节,使开发者可以使用极简的方法实现功能,大大提高开发效率 什么是三层架构? 表现层: 用于展示数据 业务层: 是处理业务需求 持久层: 和数据库进行交互 持久层技术解决方案 JDBC技术: Connection ...

    什么是框架?

    他是我们软件开发中的一套解决方案,不同的框架解决不同的问题
    使用框架的好处:
    框架封装了很多的细节,使开发者可以使用极简的方法实现功能,大大提高开发效率

    什么是三层架构?

    表现层: 用于展示数据
    业务层: 是处理业务需求
    持久层: 和数据库进行交互

    持久层技术解决方案
             JDBC技术:
                            Connection
                            PreparedStatement
                            ResultSet
              SpringJdbcTemplate:
                            Spring中对JDBC的简单封装
              Apache的DBUtils
                            和Spring的JDBCTemplate很像,都是简单封装
    
      以上都不是框架
          JDBC是规范
          Spring的JdbcTemplate和Apache的DBUtils都只是工具类
    

    Mybatis框架概述:

    是一个优秀的基于java的持久层框架,它内部封装了jdbc很多细节,所以我们只需关注sql语句本身,无需关注加载驱动,获取连接,创建statement对象等过程,使用ORM思想实现结果集的封装.

    ORM(Object Relational Mapping) :对象关系映射

    就是把数据库表和实体类的属性对应起来,让我们可以操作实体类就实现操作数据库表

    mybatis的环境搭建:

    1.创建maven工程并导入坐标
    2.创建实体类和dao的接口
    3.创建Mybatis的主配置文件
    SqlMapConfig.xml

    //约束条件
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    
    <!--mybatis的主配置文件-->
    <configuration>
        <!--配置环境-->
        <environments default="mysql">
            <!--配置mysql的环境-->
            <environment id="mysql">
                <!--配置的事务类型-->
                <transactionManager type="JDBC"></transactionManager>
                <!--配置数据源,也叫连接池-->
                <dataSource type="POOLED">
                    <!--配置连接数据库的4个基本信息-->
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                    <property name="user" value="debian-sys-maint"/>
                    <property name="password" value="YN6bICN3ccV5FbIn"/>
                </dataSource>
            </environment>
        </environments>
    
        <!--指定映射配置文件的配置,映射配置文件是指给每个Dao独立的配置文件-->
        <mappers>
            <mapper resource="com/tulun/dao/IUserDao.xml"></mapper>
        </mappers>
    </configuration>
    

    4.创建映射配置文件
    IUserDao.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.tulun.dao.IUserDao">
        <!--配置查询所有-->
        <select id="findAll">
            select *from user
        </select>
    </mapper>
    

    环境搭建的注意事项:
    1.创建IUSerDao.xml 和 IUserDao.java时名称是为了和我们之前的知识保持一致,在Mybatis中,他把持久层的操作接口名称和映射文件也叫作:Mapper
    所以IUserDao 和IUserMapper是一样的
    2.在idea创建目录的时候,他和包是不一样的, 包在创建时:com.tulun.dao他是三级目录 目录在创建时:com.tulun.dao他是一级目录
    3.mybatis的映射配置文件位置必须和dao接口的包结构相同
    4.映射配置文件的mapper标签的namespace属性的取值必须是dao接口的全限定类名
    5.映射配置文件的操作配置(select ),id属性必须是dao接口的方法名 当我们遵从了3,4,5点之后,我们在开发中就无需在写dao的实现类

    Mybatis的基本代码:

    第一步:读取配置文件
    第二步:创建SqlSessionFactory工厂
    第三步:创建SqlSession对象
    第四步:创建Dao接口的代理对象
    第五步:执行dao中的方法
    第六步:释放资源

    		//加载SqlMapConfig.xml配置文件
            InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //创建SqlSessionFactory会话工厂
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
            //创建会话
            SqlSession sqlSession = sessionFactory.openSession();
            //获取代理对象
            AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
    		//查询
            List<Account> accounts = accountDao.findAll();
    
            for(Account account : accounts) {
                System.out.println(account);
            }
    
            in.close();
            sqlSession.close();
    
    

    Mybatis的CRUD操作

    <mapper namespace="com.tulun.dao.IUserDao">
    
        <!--配置查询所有的结果的列名和实体类属性名的对应关系-->
        <resultMap id="userMap" type="com.tulun.domain.User">
            <!--配置主键字段的对应-->
            <!-- <id property="" column=""></id> -->
            <!--非主键字段的对应-->
            <!-- <result property="userId" column="id"></result> -->
            <result property="userName" column="username"></result>
            <result property="userSex" column="sex"></result>
            <result property="UserBirthday" column="birthday"></result>
            <result property="userAddress" column="address"></result>
        </resultMap>
    
        <!--查询所有-->
        <select id="findAll" resultMap="userMap">
            select *from user;
        </select>
        
        <!--保存用户  parameterType:参数的类型-->
        <insert id="saveUser" parameterType="com.tulun.domain.User">
            <!--配置插入操作后,获取插入数据的id-->
            <selectKey keyProperty="userId" keyColumn="id" resultType="Integer" order="AFTER">
                select last_insert_id();
            </selectKey>
            insert into user(id,username,birthday,sex,address)values (#{userId},#{userName},#{UserBirthday},#{UserSex},#{UserAddress});
        </insert>
        
        <!--更新用户-->
        <update id="updateUser" parameterType="com.tulun.domain.User">
            update user set username=#{userName},birthday=#{UserBirthday},address=#{UserAddress} where id=#{id};
        </update>
    
        <!--删除用户-->
        <delete id="deleteUser" parameterType="Integer">
            delete from user where id=#{id};
        </delete>
    
        <!--根据id查询用户-->
        <select id="findById" parameterType="Integer" resultMap="userMap">
            select * from user where id=#{id};
        </select>
        
        <!--根据用户名查询 模糊查询-->
        <select id="findByName" parameterType="java.lang.String" resultMap="userMap">
            <!--select * from user where username like #{username}; -->
            select * from user where username like '%${value}%'
        </select>
    
        <!--查询总用户数-->
        <select id="findTotal" resultMap="userMap">
            select count(id) from user;
        </select>
    
        <!--根据queryVo的条件查询用户-->
        <select id="findUserByVo" parameterType="com.tulun.domain.QueryVo" resultMap="userMap">
            select * from user where username like #{user.userName};
        </select>
    </mapper>
    
    

    Mybatis的参数深入:
    parameterType(输入类型):传递简单类型
    传递pojo(实体类)对象
    传递pojo包装对象 什么是ognl表达式:Object(对象) Graphic(图) Navigation(导航) Language(语言)
    它是通过对象的取值方法来获取数据.在写法上吧get省略了.比如:获取用户的名称:
    类中的写法:user.getUsername();
    ognl表达式写法:user.username; mybatis中为什么可以直接写username而不用user.呢
    因为在parameterType中已经提供了属性所属的类,所以比用些对象名.

    Mybatis的标签使用:

    properties标签:

    <!--配置properties
        可以在标签内部配置连接数据库的信息.也可以通过属性引用外部配置文件信息
        resource属性:
            用于指定配置文件的位置,是按照类路径的写法来写,必须存在类路径下.
        url属性:
            是要求按照Url的写法来写地址
            URL: Uniform Resource Locator 统一资源定位符.他是可以唯一标识一个资源的位置
            它的写法:
                http://localhost:8080/mybatisserver/demoSerlet
                协议  主机   端口     URI
            URI:Uniform Resource Identifier 统一资源标识符.他是在应用中可以唯一定位一个资源的
    -->
    
    <!--配置properties 1.通过加载配置文件的方式 配置resource属性-->
    <properties resource="jdbcConfig.properties"></properties> 
    
    <!--配置properties 2.通过配置url的方式-->
    <properties url="file:///home/fly/IdeaProjects/mybatis/mybatis_CRUD/src/main/resources/jdbcConfig.properties"></properties>
    
    

    typeAliases标签:

    
    <!--使用typeAliases 配置别名.只能配置domain中的类的别名-->
    <typeAliases>
    一. <!--typeAlias用于配置别名 type属性指定的是实体类全限定类名 ,alias属性指定别名,当指定了别名之后就不在区分大小写-->
        <typeAlias type="com.tulun.domain.User" alias="user"></typeAlias>
    
    二. <!--用于指定配置别名的包 ,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不区分大小写-->
        <package name="com.tulun.domain"/>
    </typeAliases>
    
    <!--配置映射文件-->
    <mappers>
        <mapper resource="com/tulun/dao/IUserDao.xml"></mapper>
    
        <!--package标签是指定dao接口所在的包,当指定之后就不需要再写mapper 以及resource 或者class-->
        <package name="com.tulun.dao"/>\
    </mappers>
    

    mapper里的标签(可以实现动态sql)

    if标签

    <!--根据条件查询-->
    <select id="findUserByCondition" parameterType="user" resultMap="userMap">
        select *from user where 1=1
        <if test="userName != null ">
            and username =#{userName}
        </if>
    </select>
    

    where标签

    <select id="findUserByCondition" parameterType="user" resultMap="userMap">
        select *from user
        <where>
            <if test="userName != null ">
                and username =#{userName}
            </if>
            <if test="UserSex !=null">
                and sex =#{UserSex}
            </if>
        </where>
    </select>
    

    sql标签

    <!--了解的内容 用于抽取重复的语句-->
    <sql id="defaultUser">
        select *from user;
    </sql>
    
    <!--查询所有-->
    <select id="findAll" resultMap="userMap">
        <include refid="defaultUser"></include>
    </select>
    

    foreach标签

    <!--根据queryVo的id集合 实现查询用户列表-->
    <select id="findUserInIds" resultMap="userMap" parameterType="queryVo">
        select * from user
        <where>
            <if test="ids != null and ids.size()>0">
                <foreach collection="ids" open="and id in(" close=")" item="id" separator=",">
                <!--这个id 由 item的 id 决定-->
                    #{id}
                </foreach>
            </if>
        </where>
    </select>
    
    

    连接池:

    可以减少我们获取连接所消耗的时间. (连接池就是一个用于存储连接的容器,容器就是一个集合对象,该集合必须是线程安全的,不能两个线程拿到同一个连接,该集合还必须实现队列的特性:先进先出) ```

    
    mybatis中的连接池:
        mybatis连接池提供了三种方式的配置
        配置的位置:
           主配置文件SqlMapConfig.xml中的datasource标签,type属性表示采用那种连接池的方式
           type的取值:
               POOLED :采用传统的javax.sql.DataSource规范中的连接池 ,mybatis中有针对规范的实现(相当于我们手动创造一个连接)
               UNPOOLED :采用传统的获取连接的方式,虽然也实现了javax.sql.DataSource接口,但是并没有采用池的思想.
              (拓展)   JNDI :采用服务器提供的JNDI技术实现来获取DataSource对象,不同的 服务器拿到的dataSource是不一样的.
              注意:如果不是web或者maven的war工程,是不能使用的.
        我们测试时候使用tomcat服务器,采用的连接池:dbcp连接池
    

    Mybatis的延迟加载

    问题:在一对多中,有一个用户,他有100个账户,在查询的时候,要不要吧关联的账户查出来?
    在查询用户时,用户下的账户信息应该是什么时候使用,什么时候查询
    在查询账户时,账户的所属用户信息应随着账户查询时一起查出来
    什么是延迟加载
    在真正使用数据时才发起查询,不用的时候不查询(按需加载,懒加载)
    什么是立即加载
    不管用不用,只要调用方法,马上发起查询
    在对应的四种表关系中:一对一 多对一 一对多 多对多
    一对多,多对多:通常情况下采用延迟加载.
    多对一,一对一:通常情况下采用立即加载.

    //SqlMapConfig文件:

    <!-配置参数--->
    <settings>
              <!--开启Mybatis支持延迟加载-->    
              <setting name ="lazyLoadingEnable" value = "true"/>  //默认值为true
    
              <!--当开启时,任何方法的调用都会加载该对象的所有属性-->
              <setting name ="aggressiveLazyLoading" value = "false"/></setting> 
    </settings>
    

    mybatis中的一级缓存和二级缓存

    一级缓存:

    mybatis的sqlSession对象的缓存

    但我们执行查询之后,查询的结果会同时存入到sqlSession为我们提供的区域,该区域的结构是一个map.
    当我们再一次查询同样的数据,mybatis会先去sqlSession中查看是否有,有的话直接拿出来用,
    当sqlSession对象消失后,mybatis的一级缓存也就消失了. 
    sqlSession.clearCache();可以清空缓存 
    当调用sqlSession的修改,添加,删除,commit()等方法时,就会清空一级缓存
    
         User user1 = userDao.findById(1);
         System.out.println(user1);
         sqlSession.clearCache();  //清空缓存
         //update(),commit(),delete();
         User user2 = sqlSession.getMapper(IUserDao.class).findById(1);
         System.out.println(user2);
         System.out.println(user1 == user2);
         
    打印结果:
    com.tulun.domain.User@6c64cb25
    com.tulun.domain.User@5158b42f
    false
    

    二级缓存

    指的是mybatis中的sqlSessionFactory对象的缓存,由同一个sqlSessionFactory对象创建的sqlSession共享其缓存. 二级缓存存放的是数据,不是对象

    二级缓存的使用步骤: 
    1.让mybatis框架支持二级缓存(在sqlMapConfig.xml中配置)

    <!--配置二级缓存-->
    <setting name="cacheEnabled" value="true"/>
    

    2.让当前的映射文件支持二级缓存(在IUserDao.xml中配置)

    <!--开启user支持二级缓存-->
    <cache/>
    

    3.让当前的操作支持二级缓存(在select标签中配置)

    <select id="findById" resultType="com.tulun.domain.User" useCache="true">
            select *from user where id =#{id}
    </select>
    

    Mybatis的注解开发:

    注解代码:
    SqlMapConfig.xml

    <mappers>
            <package name="com.tulun.dao"/>
    </mappers>
    

    IUserDao ( Interface )

    /**
     * 查询所有
     * @return
     */
    @Select("select *from user")
    List<User> findAll();
    
    /**
     * 保存用户
     * @param user
     */
    @Insert("insert into user(id,username,address,sex,birthday) value(#{id},#{username},#{address},#{sex},#{birthday})")
    void saveUser(User user);
    
    /**
     * 更新用户
     * @param user
     */
    @Update("update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where id =#{id}")
    void updateUser(User user);
    
    /**
     * 删除用户
     * @param id
     */
    @Delete("delete from user where id =#{id}")
    void deleteUser(Integer id);
    
    /**
     * 查询一个用户
     * @param id
     * @return
     */
    @Select("select * from user where id=#{id}")
    User findUserById(Integer id);
    
    /**
     * 根据名字查询用户
     * @param name
     * @return
     */
    @Select("select * from user where username like #{username}")
    List<User> findUserByName(String name);
    

    注意:使用注解开发的时候,就不必配置com.tulun.dao.IUserDao.xml文件,因为如果两个都有,mybatis加载的时候,就不知道该怎么办.

    IUserDao(Interface)

    举例://如果User实体类属性名和数据库属性名不一样,可以使用以下标签
    @Results(id = "userMap",value = {
            @Result(id = true, column = "id",property = "userId"),//id为主键
            @Result(column = "username",property = "userName"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "birthday",property = "userBirthday"),
            @Result(column = "address",property = "userAddress"),
    })
    /**
     * 查询所有
     * @return
     */
    @Select("select *from user")
    @ResultMap(value = {"userMap"})   //标准写法
    List<User> findAll();
    

    多表查询操作

    多对一(账户查用户)

    在mybatis中,多对一的查询就是一对一(多个账户对应一个用户 mybatis认为:一个账户对应一个用户)
    在Account实体类中就应该有一个private User user; 属性
    IAccountDao(Interface)
    @Results(id = "accountMap",value = {
            @Result(id = true, column = "id",property = "userId"),//id为主键
            @Result(column = "uid",property = "uid"),
            @Result(column = "money",property = "money"),
            @Result(property = "user",column = "uid",one = @One(select = "com.tulun.dao.IUserDao.findById",fetchType = FetchType.EAGER))
    })                         
    property: user属性   
    cloumn:根据uid在user库进行查找   
    one=@One(select ="在user库查找的方式")  
    fetchType.EAGER:立即加载方式 (还有lazy懒加载,default默认)
    

    一对多(用户查账户)

    在User实体类中就应该有一个 private List<Account> accounts; 属性
    IUserDao(Interface)
     
    @Results(id = "userMap",value = {
            @Result(id = true, column = "id",property = "userId"),//id为主键
            @Result(column = "username",property = "userName"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "birthday",property = "userBirthday"),
            @Result(column = "address",property = "userAddress"),
            @Result(property = "accounts",column = "id",many =@Many(select = "com.tulun.dao.IAccount.findAccountByUid",fetchType = FetchType.LAZY))
    })
    property: accounts属性   
    cloumn:根据id在account库进行查找   
    many=@Many(select ="在account库查找的方式")  
    fetchType.LAZY:懒加载方式
    

    缓存的配置

    mybatis中不管xml还是注解,一级缓存都是本身具备的.
    二级缓存:

     SqlSession sqlSession = factory.openSession();
     User user1 = sqlSession.getMapper(IUserDao.class).findById(1);
     System.out.println(user1);
     sqlSession.close(); //释放一级缓存
     
     SqlSession sqlSession = factory.openSession();// 再次打开sqlSession
     User user2 = sqlSession.getMapper(IUserDao.class).findById(1);
     System.out.println(user2);
    

    使开启二级缓存:
    SqlMapConfig.xml文件

    <settings>
        <!--配置二级缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    

    IUserDao接口

    @CacheNamespace(blocking = true)  //开启二级缓存
    public interface IUserDao {
    }
    
    展开全文
  • Hibernate延时加载,其实这个异常写的非常之清楚,就是会话关闭,无法对Hibernate实体进行操作。...如果对一个类或者集合配置了延迟检索策略,那么必须当代理类实例或代理集合处于持久化状态(即处于Sessi
  • 原理:通过并发连接池进行的慢速读攻击(基于TCP持久时间)等。慢速攻击基于HTTP协议,通过精心的设计和构造,这种特殊的请求包会造成服务器延时,而当服务器负载能力消耗过大即会导致拒绝服务 (由于apache漏洞引发...
  • 01 Kafka的简单介绍

    2020-04-10 11:46:00
    ​ kafka的写入操作是很快的,这主要得益于它对磁盘的使用方法不同。虽然kafka会持久化所有数据到磁盘,但本质上每次写入操作其实都只是把数据写入到操作系统的页缓存中,然后由操作系统自行决定什么时候把页缓存中...
  • 缓存方案一:EhCache

    2020-05-13 17:00:18
    EhCache是十分流行的开源缓存框架,解决了关系数据系统数据提取的高花费、高延时问题。 Ehcache 属于轻量级框架,内含持久化机制,保证JVM和服务器重启后数据不会丢失。 Hibernate和Liferay的默认缓存策略就是用...
  • HTTP基础

    2019-09-25 05:40:04
    一、TCP确保可靠性的方法:  1、三次握手;...三、HTTP协议通信过程:  HTTP1.1协议支持持久连接,即在一个TCP连接上可以传送多个HTTP请求和响应,从而减少了建立和关闭连接的消耗和延时。  简述:...
  • 场景是这样的: 1.workflowruntime启动了持久化和监听服务 2.workfllowruntime创建多个实例,并启动,一些会长时间延时,一些会中途暂停,会不同的执行状态(业务状态) 3.另有一winform控制台,有个表格,刷新显示每个实例的...
  • Mybatis

    2021-03-28 17:38:20
    基于xml配置映射关系第三天,连接池使用连接池的配置方法多表查询1、一对一查询2、一对多查询3、多对多关系查询第四天,缓存机制和延迟机制配置延迟加载一对一延迟加载一对多实现延时加载缓存的使用的 Mybatis的入门...
  • 而且加了延时还是读不到 并发大约100,每个并发写入数据大约1.5M字符串(对900KB文件做Base64后的值),本机调试,redis已关闭持久化 连接参数:"RedisConnection": &#...
  • 当短信厅联不上mas的时候就别企图立即重试,进行延时稍候再次进行重试。注意处理好短信业务超时的关系。 谢谢你的回复,我好好思考一下 <strong>问题补充</strong><br/><div class="quote_title">...
  • workflow资料

    2008-06-30 10:17:32
    │ 事件容器 EventDrivenActivity.doc │ │ 代码结点 CodeActivity.doc │ │ 同步容器 SynchronizationScopeActivity.doc │ │ 并行容器 ParallelActivity.doc │ │ 延时结点 DelayActivity.doc...
  • 《Docker下Nacos持久化配置》; spring-cloud-kubernetes特辑 《spring-cloud-kubernetes官方demo运行实战》 《你好spring-cloud-kubernetes》 《spring-cloud-kubernetes背后的三个关键知识点》 《spring-cloud-...
  • Android 上百实例源码分析以及开源分析 集合打包4

    千次下载 热门讨论 2012-07-10 21:54:03
    这是必要的,因为这些规则是不持久的。 HelpDialog对话框中显示的“帮助”菜单选项被选中时。 MainActivity 主界面 功能实现部分 PassDialog对话框中显示要求输入密码。 StatusWidget 构件实现的ON / OFF 部件状态 ...
  • Android 上百实例源码分析以及开源分析 集合打包3

    千次下载 热门讨论 2012-07-10 20:37:16
    SQLBuilder 数据库编辑器 根据参数选择不同的编辑方式 含main方法 SubjectSQLBuilder 项目数据库的编辑器 由上分析可以得出,该软件实现了数据库的操作,界面布局不复杂。 5、一款查询软件(身份证号,号码归属...
  • 2. 有一定的延时。 利用ZooKeeper有两个特性,就可以实现另一种集群机器存活性监控系统: 1. 客户端在节点 x 上注册一个Watcher,那么如果 x?的子节点变化了,会通知该客户端。 2. 创建EPHEMERAL类型的节点,一旦...
  • RQAlpha 2.0.0

    2021-01-10 08:16:58
    可以把 <code>MixedPortfolio</code> 看作是一个黑盒的 <code>portfolio, 我们实现了 <code>portfolio</code> 接口定义中所有的方法和属性。而且为了支持扩展性(即增加新的可交易品种,比如期权、比特币等)&#...
  • = 1.2.47 远程命令执行漏洞利用工具及方法 SpringBoot_Actuator_RCE jizhicms(极致CMS)v1.7.1代码审计-任意文件上传getshell+sql注入+反射XSS CVE-2020-9484:Apache Tomcat Session 反序列化代码执行漏洞|CVE-2020...
  • 带宽、延时、吞吐率、PPS 这些都是啥? 图解HTTP协议 一文领略 HTTP 的前世今生 面试 HTTP ,99% 的面试官都爱问这些问题 实战!敖丙用“大白鲨”让你看见 TCP 面试官:换人!他连 TCP 这几个参数都不懂 TCP/IP 基础...

空空如也

空空如也

1 2
收藏数 26
精华内容 10
关键字:

持久延时方法