所有的持久化类必须要有一个无惨构造器,因为hibernate是使用java反射机制为我们创建对象的。构造器的访问级别可以是private,然而当生成代理的时候至少使用的是package级别的访问控制
hibernate的核心配置文件可以是properties或xml(hibernate.properties或hibernate.cfg.xml)
:表示该字段不映射到DB中,该注解无属性
@OneToMany(mappedBy=“在多的一方中一的属性名”):该属性是让一的一方放弃关联关系的维护,由多的一方去维护关联关系。该注解只能用在双向关联中。该注解不能与@JoinColumn注解同时使用,因为两者的作用相反。
解决Hibernate4执行update操作,不更新数据的问题,后台封装java对象,使用hibernate4再带的update,执行不更新数据,不报错。需要自己编写sql,调用session.createQuery(sql).setParamter().executeUpdate();
多对多关联使用@ManyToMany注解。其会自动生成一个中间表,表名为两个关联对象的映射表名的联合:表1_表2。只不过,谁在维护关联关系,谁的表名在前。
该中间表会包含两个字段,字段名与谁在维护关联关系相关。谁在维护关联关系,谁的表名将出现在第一个字段名中,而该类的关联属性名将出现在第二个字段名中。字段名分别为表名_id与关联属性名_id。
当然,默认的表名和字段名均可通过@JoinTable进行修改
多对多双向关联时,需要某一方放弃关联关系维护权。否则,将会生成两张中间表
Hibernate是DAO层技术,对数据的使用,查询是最为重要的。Hibernate的查询技术非常强大,支持原始SQL语句查询,支持QBC查询以及Hibernate特有的HQL查询。
HQL,Hibernate Query Language,Hibernate查询语言,它和SQL非常相似,但是,HQL是面向对象的查询语言,而SQL是面向二维表的。HQL查询语句中使用的是类名和属性名,而SQL语句使用的是表名和字段名。
需要注意的是,在不使用类的别名的情况下,在HQL中直接使用字段名也是可以通过的。但是若使用“类别名.属性名"的形式,则不能够使用字段名。
QBC,Query By Criteria,标准查询,一种比HQL更为面向对象的查询方法。
Hibernate进行HQL查询的接口,支持动态绑定参数的功能。
使用Session对象的createQuery方法可获取Query对象。
Query query = session.createQuery(hql);。
Hibernate进行SQL原生查询的接口,支持动态绑定参数的功能,是Query接口的子接口。使用Session对象的createSQLQuery()方法可获取SQLQuery对象。
SQLQuery sqlQuery = session.createSQLQuery(sql);。
其查询出的结果对象默认为Object,当然,若结果为List,则其元素为Object。使用SQLQuery的addEntity(Xxx.class)方法,可以将其结果泛型设定为指定类型。
Criteria标准、准则,Hibernate进行Criteria查询的接口,与Query接口无关。使用Session对象的createCriteria()方法可获取Criteria对象。
Criteria criteria = session.createCriteria(Xxx.class);。
1、使用list(),会一次性将所有符合条件的记录查询出来,而使用iterate(),则首先会查询所有符合条件的记录的id,然后在根据这些id逐个查询出记录的具体内容。
2、使用list(),不会使用缓存机制,即每次执行一次查询代码,控制台会执行一次SQL查询语句;而使用iterate(),则会使用缓存机制,只有第一次会执行SQL查询,再往后的查询会直接从缓存中读取
使用Query的iterate()方法虽然使用了Hibernate的缓存机制,但是同时也出现了N+1问题。
所谓N+1问题是指,从查询出有效数据角度来说,若要查询符合某条件的对象时,使用list(),则一次性即可查询出所有。而若使用iterate(),则会先查询出所有满足条件的对象的id,然后再逐个id进行select查询,即需要经过N+1次才能得出有效结果。这就是N+1问题。
若想要使用Hibernate缓存,即使用iterate()方法,还想要避免N + 1问题,就要保证在执行iterate()时,缓存中已经有数据,这样既可利用缓存,有可以避免N+1问题。所以,可以在第一次查询时使用list(),而以后的查询则使用iterate()。
若关联关系是由“一方”来维护,在插入时,"一方"主动联系"多方",需要在"一方"对象产生之前在关联的“多方”中插入一条数据,但此时"多方"的外键时null,当"一方"对象产生并插入成功之后,再使用update语句来修改"多方"的外键
若关联关系是由"多方"来维护,在插入时,“多方”主动联系"一方",在插入"多方"之前,现在插入"一方",然后再插入"多方"。这时"多方"的外键时插入进去的
具有关联关系的实体对象间的加载和访问关系是单向的。即只有一个实体对象可以加载和访问对象,但是对方是看不到另一方的。
指具有关联关系的实体对象间的加载和访问关系是双向的。即任何一方均可加载和访问另一方. 在想要维护关系的一方的对方注解上添加mapped属性。在本方注解中添加cascade = CascadeType.ALL:指定级联类型
以双向关联为例,在定义实体类的toString()方法时需要注意,对关联属性的输出,最好是只有一方进行输出,而另一方不进行关联属性输出。因为双方均进行输出,有可能出现循环引用问题,会抛出栈溢出错误StackOverflowError。
通常使用Session提供的两个方法:get()和load()。默认情况下,
get()为直接加载,当代码中出现get()时,后台会马上调用执行select语句,将对象直接加载到内存。
load()为延迟加载。当代码中出现load()时,后台并不会马上调用执行select。只有当代码中真正要访问除了对象的主键id属性以外的其他属性时,即真正要访问对象的详情时,才会真正执行select语句,即此时才会将对象真正加载到内存中。
hibernate 的延迟加载使用的时CGLIB实现
事务范围缓存,单session缓存,一级缓存
应用范围缓存,单sessionFactory缓存,二级缓存
集群范围缓存,多sessionFactory缓存
一级缓存,就是Session缓存,其实就是内存中的一块空间,在这个内存空间中存放了相互关联的Java对象。
Session缓存是事务级缓存,伴随着事务的开启而开启,伴随着事务的关闭而关闭。Session缓存由Hibernate进行管理。
Session缓存,是Hibernate内置的。是不能被程序员取消的。即,只要使用Hibernate,就一定要使用,更确切地说,就一定在使用Session缓存。
当程序调用Session的load()方法时,get()方法、save()方法、saveOrUpdate()方法、update()方法或者是Query方法时,Hibernate会对实体对象进行缓存。
当通过get()或者load()方法查询实体对象时,Hibernate会首先到缓存中查询,在找不到实体对象的情况下,Hibernate才会发出SQL语句到DB中查询。从而提高Hibernate的使用效率。
快照,即副本。Hibernate中的快照,即数据库的副本。快照中的数据由Hibernate自己维护。快照中的数据始终保持和DB中数据一致,不过由代码对副本中的内容进行修改。其作用主要是为了在处理数据更新时,将session中的数据与DB中的数据始终保持一致),以此来判断是否真正执行update语句。
当代码通过session的查询方法调用,将数据加载到内存后,框架会将此数据存于Session缓存中,当然快照中也有该数据的副本。session缓存中数据是可以修改的,但是快照中的数据时不能够修改的,始终保持和DB中数据一致。默认情况下,当事务在提交时,会首先对比Session缓存中数据和快照中的数据。若不一致,则说明数据发生更新,会将Session缓存中数据通过update语句更新至DB中。当然,快照中数据也会更新。若一致,则说明数据未发生改变,无需做数据同步。
二级缓存是SessionFactory级别的缓存,其生命周期与SessionFactory一致。SessionFactory缓存可以依据功能和目的不同而划分为内置缓存和外置缓存。
SessionFactory的内置缓存中存放了映射元数据和预定义SQL语句,映射元数据是映射文件中数据的副本,而预定义SQL语句是在Hibernate初始化阶段根据映射元数据推导出来的SQL。SessionFactory的内置缓存是只读的,应用程序不能够修改缓存中的映射元数据和预定义SQL语句,因此SessionFactory不需要进行内置缓存和映射文件的同步。
SessionFactory的外置缓存是一个可配置的插件。在默认情况下,SessionFactory不会启用这个插件。外置缓存的数据是数据库数据的副本,外置缓存的介质可以是内存或者硬盘。SessionFactory的外置缓存也称之为Hibernate的二级缓存。
HIbernate本身只提供了二级缓存的规范,但是并非实现,故需要第三方缓存产品的支持,常用的二级缓存第三方插件有:EHCache、Memcached、OSCache、SwarmCache、JBossCache 等。这些插件的功能各有侧重,各有特点。
当Hibernate根据ID访问数据对象时,首先会从一级缓存Session中查找。若查不到且配置了二级缓存,则会从二级缓存中查找;若还是没有查到,就会查询数据库,把结果按照ID放入到缓存中。
执行增、删、改操作时,会同步更新缓存。
事务型(transactional):隔离级别最高,对于经常被读但是很少被改的数据,可以采用此策略。因为它可以防止脏读和不可重复读的并发问题。发生异常的时候,缓存也能够回滚(系统开销大)。
读写型(read-write):对于经常被读但是很少被改的数据,可以采用此策略。因为它可以防止脏读的并发问题。更新缓存的时候会锁定缓存中的数据。在EHCache中,使用该策略,将无法从二级缓存中获取数据。
非严格读写型(nonstrict-read-write):不保证缓存和数据库中的数据的一致性。对于极少被改,并且允许偶尔脏读的数据,可采用此策略,不锁定缓存中的数据。
只读型(read-only):对于从来不会被修改的数据,可使用此策略。
当二级缓存使用ehcache时,@Cache(usage = CacheConcurrencyStrategy.READ_WRITE),为了保证读到的数据时最新的数据,将无法从二级缓存中直接获取数据.
Query查询结果会存放与缓存中,第二次查询会从缓存中获取,提前是同一个session。第一次使用session.createQuery(sql),第二次使用session.get().
同一个session,但是两次查询都是用了session.createQuery()也不会从缓存中查询
如果要使用Query缓存,首先要在hibernate.properties开启Query缓存的总开关,hibernate.cache.use_query_cache=true,然后每一次使用Query查询是还需要调用query.setCacheable(true);要求sql语句完全相同。
绕过一级缓存的修改,session.createQuery(sql).executeUpdate();executeUpdate()方法可以绕过一级缓存直接修改DB。
在二级缓存中存放的对象,比一级缓存中多出一个属性:updateTimeStamp,修改时间戳。只要这个属性发生改变,就说明有操作修改了DB中的数据,二级缓存中的该缓存对象已经不是最新数据,需要从DB中再次查询更新。
而Query接口的executeUpdate()方法所进行的更新,可以绕过一级缓存,但是会修改二级缓存中缓存对象的updateTimeStamp值。而该值的改变,使得本例在清空session后,又进行了一次查询。其实,只要执行executeUpdate()方法,都会引发后台进行update操作,都会引发updateTimeStamp值的改变,都会使二级缓存通过新的查询来更新数据。无论是否真的有数据更新。
原子性:(atomicity)事务中的全部操作在数据库中是不可分割的,要么全部完成,要么均不执行。
一致性:(Consistency)几个并行执行的事务,其执行结果必须与按某一顺序串行执行的结果相一致。
隔离性(Isolation):事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的。
持久性(Durability):对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。
读并发:读上有三类并发问题:脏读、不可重复读、幻读;
写并发:写上有两类并发问题:第一类丢失更新(回滚丢失更新),第二类丢失更新(提交丢失更新)
A事务读取了B事务未提交的数据。
说明:对于事务隔离级别设置较低的数据库,是允许将未提交的数据写入到数据库的。但是,即使写入到数据库中,若事务回滚,也是可以再将数据恢复为原数据的。
所以,可能发生脏读的现象:A事务修改了某数据,但是还未提交。此时,B事务读取了该数据。但是此时A事务又发生了回滚操作。那么,B事务读取到的就是个“不存在”的脏数据。
读取了已提交的事务,A事务先读取了一个数据,而后B事务修改(update或者delete)该数据并提交。此时A事务再次读取该数据时,该数据已经别修改或者不存在。即无法再读到原来相同的数据。
也叫虚读。读取了已提交的事务。与不可重复读不同的是,发生了插入(insert)操作。A事务先进行了某一条件的检索操作,而后B事务插入了若干数据并提交。这些数据存在符合A事务检索条件的数据。此时A事务再做相同检索,其检索结果就会与第一次的不同。在第二次取出了“幻影”。
读取未提交:不繁殖任何并发问题。
读取已提交:防止脏读,可能出现不可重复读或者幻读。
可重复读:防止脏读和不可重复读,可能会出现幻读。
串行化:不存在并发问题。
MySql默认的事务隔离级别为可重复读,即防止了脏读和不可重复读,但是有可能存在幻读现象。通过 select @@global.tx_isolation;可以查询MySql默认的事务隔离级别。
第一类丢失更新:也称之为回滚丢失更新。A、B事务同时读取某数据,并均做修改,A事务进行了提交,而B事务又做回滚。此时,A事务提交的更新数据丢失。
第二类丢失更新:也称之为提交丢失更新。A、B事务同时读取某个数据,并均做修改。A事务先做了提交,然后B事务也做了提交。此时A事务提交的更新数据会被B事务的提交给覆盖。
通过加锁可以解决写并发问题。锁可以分为两类:
乐观锁(Optimistic lock):每次访问数据时,都会乐观的认为其他事务此时肯定不会同时修改该数据。但是在真正修改时,会在代码中先判断数据是否已经被其他事务修改过。所以锁是加在代码中的。
悲观锁(Pessimistic lock):每次访问数据时,都会悲观的认为其他事务一定会同时修改该数据。所以,其在访问数据时,在数据库中就会先给数据加锁,以防止其他事务同时修改该数据。所以锁是加在数据库中的
乐观锁是加在代码中的锁机制,一般充当乐观锁的有两类数据:版本号和时间戳。它们的工作原理是相同的。
A、B事务从DB中读取数据时同时会读出一个数据版本号,当A事务将修改过的数据写入到DB中时,会使得版本号增加1,当B事务发生回滚或者覆盖时,会首先对自己数据的版本号与DB中数据的版本号进行对比。若他们相等,则说明DB中数据没有发生变化,B事务可以将数据回滚到原始状态,或者将修改写入到DB中。若小于DB中的版本号,则说明其他事务已经修改过该数据,将抛出异常。
悲观锁是加在DB中的锁机制,有分为两种。
写锁,又称之为排他锁,当A事务对某数据加上排他锁后,A事务将独占该数据,可对该数据进行读、写操作。但是其他事务时不能再为该数据添加任何锁的,直到A事务将排他锁解锁,将数据释放。
在SQL语句中,若要为本次操作(事务)添加排他锁,则可在正常的SQL语句最后添加上for update即可。例如:select * from student where age <20 for update
读锁,又称之为共享锁。当A事务对某数据加上共享锁后,只能对数据进行读操作。但是其他事务也同时可以为该数据添加共享锁,读取该数据。但是不能够添加写锁,直到所有事务将其共享锁解锁,将数据释放,才可再对数据添加排他锁。
在SQL语句中,若要为本次操作(事务)添加共享锁。则可在正常的SQL语句最后添加上lock in share mode即可。例如:select * from student where age < 20 lock in share mode
hibernate实现乐观锁,在其类的代表版本属性上添加@Version注解。
hibernate实现悲观锁,只需要在session.get()方法第三个参数中指定一个常量即可。
排他锁为常量:LockMode.PESIMISTIC_WRITE,解锁时间为事务结束
共享锁为常量:LockMode.PESIMISTIC_READ,解锁时间为查询结束,与事务无关
java内存模型是跟并发有关的,它屏蔽底层细节而提出的规范,希望在(Java层面上)在操作内存时在不同的平台上也有相同的效果
Java内存结构(又称为运行时数据区域),它描述着当我们的class文件加载至虚拟机后,各个分区的「逻辑结构」是如何的,每个分区承担着什么作用。
HQL查询语句中只能出现类名和属性名,不能出现表名和字段名,不能出现*,只能写成(from 类名 session.createQuery(“from 类名”))
HQL查询语句中出现的类名,必须是进行了对象关系映射的类,没有映射的类不能出现在HQL语句中
HQL查询语句中如果要使用别名,必须使用as关键字,不能省略
HQL查询语句中如果要查询个别字段,则可以session.createQuery(“select new Student(name,age) from Student”)
实现类中使用Hibernate 在正常的Hibernate中,通过Hibernate中的SessionFactory来创建session,利用Session来进行增删改查的操作. 而通过Jpa整合后Session的功能被Jpa自带的EntityManager进行了封装.
1.通过 :参数名称 的方式进行传递,注入通过 query.setParameter("参数名称",对应值)进行注入,参数名称须保持相同.
2.通过 ? 的方式进行传递,注入通过 query.setParameter("参数位置",对应值)进行注入,例如query.setParameter(1,user.getId());(注最新的hibernate需要将位置标记在?后面,例如 ?1)
Criteria是Hibernate提出的纯粹的面向对象的查询方式,在使用Criteria查询数据时,不需要书写任何一个SQL语句或者HQL语句,即时是一个完全不懂SQL的人,也完全可以使用Criteria所提供的的各种API来设置查询条件、分页、排序、联合查询等等,也依然可以使用Criteria查询出想要的数据。
这个是JPA中的注解,PersistenceContext,称为持久化上下文,它一般包含有当前事务范围内的,被管理的实体对象(Entity)的数据。每个EntityManager,都会跟一个PersistenceContext相关联。PersistenceContext中存储的是实体对象的数据,而关系数据库中存储的是记录。
@Resource
是注入容器提供的资源对象,比如SessionContext MessageDrivenContext。或者你那个name指定的JNDI对象
可以理解为资源->数据源->也就是数据连接,基本上就是告诉程序数据库在哪里
首先@PersistenceContext是jpa专有的注解,而@Autowired是spring自带的注释
上方图片的意思就是EntityManager不是线程安全的,当多个请求进来的时候,spring会创建多个线程,而@PersistenceContext就是用来为每个线程创建一个EntityManager的,而@Autowired就只创建了一个,为所有线程共用,有可能报错
获取持久化实现者的引用
用过getDelegate( )方法,你可以获取EntityManager 持久化实现者的引用,如Jboss EJB3 的持久化产品采用Hibernate,可以通过getDelegate( ) 方法获取对他的访问,如:
@PersistenceContext
protected EntityManager em;
HibernateEntityManager manager = (HibernateEntityManager)em.getDelegate();
获得对Hibernate 的引用后,可以直接面对Hibernate 进行编码,不过这种方法并不可取,强烈建议不要使用.在Weblogic 中,你也可以通过此方法获取对Kodo 的访问
另:映射的表名或列名与数据库保留字同名时的处理
将表名加标式符,例如:在Mysql下,用’order’,或在sqlserver下用[TableName],但这样做不适合程序移植
表所对应的实体必须要放在同一个包下,否则自动创建表时会报错
调用session.save()方法:
这个session,必须是sessionFactory.getCurrentSession();不能是sessionFactory.openSession().否则事务不能自动提交,同时session也不能自动关闭
必须要在applicationContext.xml中配置事务自动提交,hibernate.connection.autocommit=true
其次要配置事务管理器,配置事务驱动,然后再service层方法上添加@Transactional
getCurrentSession和openSession的区别
openSession是打开一个新的session,而getCurrentSession则是获取当前线程里的session,如果没有才打开新的。
openSession不需要配置,而getCurrentSession需要配置
thread
openSession需要手动关闭,而getCurrentSession系统自动关闭
openSession出来的session要通过:session.close(),
而getSessionCurrent出来的session系统自动关闭,如果自己关闭会报错
Session是线程不同步的,要保证线程安全就要使用getCurrentSession
@OneToOne( mappedBy = “phone”,cascade = CascadeType.ALL,orphanRemoval = true,fetch = FetchType.LAZY)
private PhoneDetails details;
删除phone的时候,要删除多。
只设置cascade = CascadeType.ALL 会删除多
只设置orphanRemoval = true 也会删除多
2个都设置,更加的会删除多。 "一方"不维护关系,"多方"将外键置为null,若"多方"维护关系,则在删除"一方"时,“多方"也会删除
orphanRemoval = true 这个一般加在一方。
cascade = CascadeType.ALL:在保存"一方"的时候,会自动保存"多方”,不需要在另外执行保存"多方"的方法
删除时,必须要有事务
类似于JDBC的占位符,只是hibernate的?下标从0开始,而JDBC的下标从1开始,基本上所有的编程索引都从0开始,唯独JDBC从1开始
mappedBy属性是hibernate双向关系中的基本概念。mappedBy的真正作用就是指定由哪一方来维护之间的关联关系。当一方中指定了mappedBy=“users”(默认),表示不放弃关系维护,那么那一方就有责任负责之间的关联关系,
说白了就是hibernate如何生成Sql来维护关联的记录! Hibernate仅仅按照主控方对象的状态的变化来同步更新数据库。
按照原来的映射文件,customer.getSetLinkMan().add(linkMan),即主控方对象的状态发生了改变,因此数据库会跟着对象状态的变化来同步更新数据库;
而customer.getSetLinkMan().add(linkMan),即被控方对象的状态发生了改变,它是不能触发对象和数据库的同步更新,这就使得linkMan联系人表外键无法自动更新。
级联保存
在"一方"有@OneToMany(cascade = CascadeType.ALL,orphanRemoval = true)和@JoinColumn(“外键”)
在"多方"没有一方的引用和@ManyToOne()
这种情况在保存"一方"的同时会保存"多方",但是外键没有指定,需要单独修改外键
插入"一方",返回的对象中有主键,"多方"集合中主键自动回填,这样就可以执行下面的update 外键操作了
@ManyToOne(optional属性)
如果设置为false,外键必须不为null
@OneToMany(orphanRemoval=true) @OneToOne(orphanRemoval=true)一对多 一对一自动监测删除,自动删除孤儿
@Basic(fetch=FetchType,optional=true)
@Basic 表示一个简单的属性到数据库表的字段的映射 , 对于没有任何标注的 getXxxx() 方法 , 默认即为 @Basic
fetch: 表示该属性的读取策略 , 有 EAGER 和 LAZY 两种 , 分别表示主支抓取和延迟加载 , 默认为 EAGER.
optional: 表示该属性是否允许为 null, 默认为 true
级联保存时,如果保存"多方",可以调用setParent()设置"一方",保存成功后,数据库表中会保存外键
使用fastjson将字符串转javaBean时,该bean必须要有setter方法,否则转换失败
@Transactional注解:
只对public修饰的方法有效,对于父类中的public方法事务不起作用,如果需要事务需要在父类上或者父类的方法上添加该注解
spring事务通过aop实现,生成代理类,代理类中有一个ThreadLocal,保存了session
事务在那个方法中开启的,就必须在那个方法中关闭
如果一个service方法没有加@Transactional注解,但却在方法里调用了一个本类添加了@Transactional注解的方法,事务不生效
异常被catch捕获导致@Transactional失效
hibernate 对象实例的三种状态
瞬时状态(Transient):对象刚刚被new创建出来,只是一个普通的类对象。存在于堆内存。
持久化状态(Persisitent):持久化对象与一个Hibernate Session相关联,在这个状态下,对象的所有属性值的改动,都是可以在事务结束时提交到数据库中的
托管状态(Detached):原本处于持久状态的对象因为其对应的Session被关闭,而失去持久化能力。此时的对象就处于脱管状态。一旦有Session愿意关联脱管对象,那么该对象就可以立马变为持久状态。保存到数据库
OpenSessionInViewFilter:过滤器。它允许在事务提交之后延迟加载显示所需要的对象。
在web.xml中添加这个filter,指定sessionFactory参数
原理:在当前线程中从sessionFactory中获取session,如果没有就创建一个session,并且将sessionFactory作为key,session作为value存放与HashMap中,如何将这个hashMap存放与当前线程的ThreadLocal中.
这样做在controller,jsp,servlet中就可以获取到设置了懒加载的集合对象(这个时候service方法事务已经关闭,但这个关闭并不是真正的关闭,真正的关闭是在OpenSessionInViewFilter中关闭的)
Hibernate中@Embedded和@Embeddable注解的使用
一个实体类要在多个不同的实体类中进行使用,而本身又不需要独立生成一个数据库表.
分别使用这两个注解作用是相同的
@Embeddable:加在实体类上
@Embeddable
public class Address implements Serializable{}
这个注解和@AttributeOverrides和@AttributeOverride一起使用,用来覆盖@Embeddable类中字段的属性的
@Embedded:加在属性上
@Embedded
private Address address;
SpringBootServletInitializer
SpringBoot项目启动两种方式:
第一种:直接运行main方法
第二种:启动类继承SpringBootServletInitializer,重写configure方法,使用外置tomcat启动
jar和war启动区别
jar:执行SpringBootApplication的run方法,其从IOC容器,创建嵌入式Servlet容器
war:先启动Servlet服务器,然后服务器启动SpringBoot,最后启动IOC容器
多项目,一些项目直接打成jar,提供其他项目使用,其他项目需要向外提供服务,所以需要打成war,这些jar直接放到了war包的WEB-INF/lib包下
所以当jar包中修改代码或者添加依赖时(父工程依赖改变时,父工程也要本地安装),必须要将这些jar重新安装到本地(在当前项目文件下执行安装命令)
1). mvn clean
2). mvn package -Dmaven.test.skip=true 打包
3). mvn install -Dmaven.test.skip=true 把项目变成依赖放到本地仓库中
然后再次将提供服务的项目打成war,部署即可