Hibernate注解完整文档
第 2 章 实体Bean | ||
---|---|---|
每一个持久化POJO类都是一个实体bean,这可以通过在类的定义中使用@Entity注解来进行声明:
@Entity public class Flight implements Serializable {Long id;@Idpublic Long getId() { return id; }public void setId(Long id) { this.id = id; } }
@Entity @Table(name="tbl_sky") public class Sky implements Serializable { ...
@Table(name="tbl_sky",uniqueConstraints = {@UniqueConstraint(columnNames={"month", "day"})})
你可以在实体bean中使用@Version注解,通过这种方式可添加对乐观锁定的支持:
@Entity public class Flight implements Serializable { ...@Version@Column(name="OPTLOCK")public Integer getVersion() { ... } }
上面这个例子中,version属性将映射到 OPTLOCK列, entity manager使用该字段来检测更新冲突(防止更新丢失,请参考last-commit-wins策略).
根据EJB3规范,version列可以是numeric类型(推荐方式)也可以是timestamp类型. Hibernate支持任何自定义类型,只要该类型实现了UserVersionType.
public transient int counter; //transient propertyprivate String firstname; //persistent property@Transient String getLengthInMeter() { ... } //transient propertyString getName() {... } // persistent property@Basic int getLength() { ... } // persistent property@Basic(fetch = FetchType.LAZY) String getDetailedComment() { ... } // persistent property@Temporal(TemporalType.TIME) java.util.Date getDepartureTime() { ... } // persistent property @Enumerated(EnumType.STRING) Starred getNote() { ... } //enum persisted as String in database
注意
为了启用属性级的延迟获取,你的类必须经过特殊处理(instrumented): 字节码将被织入原始类中来实现延迟获取功能, 详情参考Hibernate参考文档.如果不对类文件进行字节码特殊处理, 那么属性级的延迟获取将被忽略.
推荐的替代方案是使用EJB-QL或者Criteria查询的投影(projection)功能.
Hibernate和EJB3都支持所有基本类型的属性映射. 这些基本类型包括所有的Java基本类型,及其各自的wrapper类和serializable类. Hibernate Annotations还支持将内置的枚举类型映射到一个顺序列(保存了相应的序列值) 或一个字符串类型的列(保存相应的字符串).默认是保存枚举的序列值, 但是你可以通过@Enumerated注解来进行调整(见上面例子中的note属性).
在核心的Java API中并没有定义时间精度(temporal precision). 因此处理时间类型数据时,你还需要定义将其存储在数据库中所预期的精度. 在数据库中,表示时间类型的数据有DATE,TIME, 和TIMESTAMP三种精度(即单纯的日期,时间,或者两者兼备). 可使用@Temporal注解来调整精度.
@Lob注解表示属性将被持久化为Blob或者Clob类型, 具体取决于属性的类型, java.sql.Clob, Character[], char[] 和 java.lang.String这些类型的属性都被持久化为Clob类型, 而java.sql.Blob,Byte[],byte[] 和 serializable类型则被持久化为Blob类型.
@Lob public String getFullText() {return fullText; }@Lob public byte[] getFullCode() {return fullCode; }
如果某个属性实现了java.io.Serializable同时也不是基本类型, 并且没有在该属性上使用@Lob注解, 那么Hibernate将使用自带的serializable类型.
使用 @Column 注解可将属性映射到列. 使用该注解来覆盖默认值(关于默认值请参考EJB3规范). 在属性级使用该注解的方式如下:
不进行注解
和 @Basic一起使用
和 @Version一起使用
和 @Lob一起使用
和 @Temporal一起使用
和 @org.hibernate.annotations.CollectionOfElements一起使用 (只针对Hibernate )
@Entity public class Flight implements Serializable { ... @Column(updatable = false, name = "flight_name", nullable = false, length=50) public String getName() { ... }
在上面这个例子中,name属性映射到flight_name列. 该字段不允许为空,长度为50,并且是不可更新的(也就是属性值是不变的).
上面这些注解可以被应用到正规属性上例如@Id 或@Version属性.
@Column(name="columnName"; (1)boolean unique() default false; (2)boolean nullable() default true; (3)boolean insertable() default true; (4)boolean updatable() default true; (5)String columnDefinition() default ""; (6)String table() default ""; (7)int length() default 255; (8)int precision() default 0; // decimal precision (9)int scale() default 0; // decimal scale
(1) |
name 可选,列名(默认值是属性名) |
(2) |
unique 可选,是否在该列上设置唯一约束(默认值false) |
(3) |
nullable 可选,是否设置该列的值可以为空(默认值false) |
(4) |
insertable 可选,该列是否作为生成的insert语句中的一个列(默认值true) |
(5) |
updatable 可选,该列是否作为生成的update语句中的一个列(默认值true) |
(6) |
columnDefinition 可选: 为这个特定列覆盖SQL DDL片段 (这可能导致无法在不同数据库间移植) |
(7) |
table 可选,定义对应的表(默认为主表) |
(8) |
length 可选,列长度(默认值255) |
(8) |
precision 可选,列十进制精度(decimal precision)(默认值0) |
(10) |
scale 可选,如果列十进制数值范围(decimal scale)可用,在此设置(默认值0) |
@Entity public class Person implements Serializable {// Persistent component using defaultsAddress homeAddress;@Embedded@AttributeOverrides( {@AttributeOverride(name="iso2", column = @Column(name="bornIso2") ),@AttributeOverride(name="name", column = @Column(name="bornCountryName") )} )Country bornIn;... }
@Embeddable public class Address implements Serializable {String city;Country nationality; //no overriding here }
@Embeddable public class Country implements Serializable {private String iso2;@Column(name="countryName") private String name;public String getIso2() { return iso2; }public void setIso2(String iso2) { this.iso2 = iso2; }public String getName() { return name; }public void setName(String name) { this.name = name; }... }
@Embedded@AttributeOverrides( {@AttributeOverride(name="city", column = @Column(name="fld_city") ),@AttributeOverride(name="nationality.iso2", column = @Column(name="nat_Iso2") ),@AttributeOverride(name="nationality.name", column = @Column(name="nat_CountryName") )//nationality columns in homeAddress are overridden} )Address homeAddress;
Hibernate现在支持在嵌入式对象中使用关联注解(如@*ToOne和@*ToMany). 而EJB3规范尚不支持这样的用法。你可以使用@AssociationOverride注解来覆写关联列.
- 如果属性为单一类型,则映射为@Basic
- 否则,如果属性对应的类型定义了@Embeddable注解,则映射为@Embedded
- 否则,如果属性对应的类型实现了Serializable, 则属性被映射为@Basic并在一个列中保存该对象的serialized版本
- 否则,如果该属性的类型为java.sql.Clob 或 java.sql.Blob,则作为@Lob并映射到适当的LobType.
- AUTO - 可以是identity column类型,或者sequence类型或者table类型,取决于不同的底层数据库.
- TABLE - 使用表保存id值
- IDENTITY - identity column
- SEQUENCE - sequence
和EJB3规范相比,Hibernate提供了更多的id生成器.详情请查阅 Hibernate Annotation Extensions .
下面的例子展示了使用SEQ_STORE配置的sequence生成器
@Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE") public Integer getId() { ... }
下面这个例子使用的是identity生成器
@Id @GeneratedValue(strategy=GenerationType.IDENTITY) public Long getId() { ... }
AUTO生成器适用于可移植的应用(在多个DB间切换). 多个@Id可以共享同一个identifier生成器,只要把generator属性设成相同的值就可以了. 通过@SequenceGenerator 和@TableGenerator,你可以配置不同的identifier生成器. 每一个identifier生成器都有自己的适用范围,可以是应用级(application level)和类一级(class level). 类一级的生成器在外部是不可见的, 而且类一级的生成器可以覆盖应用级的生成器. 应用级的生成器则定义在包一级(package level)(如package-info.java):
@javax.persistence.TableGenerator(name="EMP_GEN",table="GENERATOR_TABLE",pkColumnName = "key",valueColumnName = "hi"pkColumnValue="EMP",allocationSize=20 ) @javax.persistence.SequenceGenerator(name="SEQ_GEN",sequenceName="my_sequence" ) package org.hibernate.test.metadata;
如果在org.hibernate.test.metadata包下面的 package-info.java文件用于初始化EJB配置, 那么该文件中定义的 EMP_GEN 和SEQ_GEN都是应用级的生成器.EMP_GEN定义了一个使用hilo算法 (max_lo为20)的id生成器(该生成器将id的信息存在数据库的某个表中.). id的hi值保存在GENERATOR_TABLE中. 在该表中pkColumnName"key"等价于 pkColumnValue "EMP", 而valueColumnName "hi"中存储的是下一个要使用的最大值.
SEQ_GEN则定义了一个sequence 生成器, 其对应的sequence名为 my_sequence. 注意目前Hibernate Annotations还不支持sequence 生成器中的 initialValue和 allocationSize参数.
下面这个例子展示了定义在类范围(class scope)的sequence生成器:
@Entity @javax.persistence.SequenceGenerator(name="SEQ_STORE",sequenceName="my_sequence" ) public class Store implements Serializable {private Long id;@Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE")public Long getId() { return id; } }
在这个例子中,Store类使用名为my_sequence的sequence,并且SEQ_STORE 生成器对于其他类是不可见的. 注意在org.hibernate.test.metadata.id包下的测试代码有更多演示Hibernate Annotations用法的例子..
下面是定义组合主键的几种语法:
- 将组件类注解为@Embeddable,并将组件的属性注解为@Id
- 将组件的属性注解为@EmbeddedId
- 将类注解为@IdClass,并将该实体中所有属于主键的属性都注解为@Id
对于EJB2的开发人员来说 @IdClass是很常见的, 但是对于Hibernate的用户来说就是一个崭新的用法. 组合主键类对应了一个实体类中的多个字段或属性, 而且主键类中用于定义主键的字段或属性和 实体类中对应的字段或属性在类型上必须一致.下面我们看一个例子:
@Entity @IdClass(FootballerPk.class) public class Footballer {//part of the id key@Id public String getFirstname() {return firstname;}public void setFirstname(String firstname) {this.firstname = firstname;}//part of the id key@Id public String getLastname() {return lastname;}public void setLastname(String lastname) {this.lastname = lastname;}public String getClub() {return club;}public void setClub(String club) {this.club = club;}//appropriate equals() and hashCode() implementation }@Embeddable public class FootballerPk implements Serializable {//same name and type as in Footballerpublic String getFirstname() {return firstname;}public void setFirstname(String firstname) {this.firstname = firstname;}//same name and type as in Footballerpublic String getLastname() {return lastname;}public void setLastname(String lastname) {this.lastname = lastname;}//appropriate equals() and hashCode() implementation }
如上, @IdClass指向对应的主键类.
Hibernate支持在组合标识符中定义关联(就像使用普通的注解一样),而EJB3规范并不支持此类用法.
@Entity @AssociationOverride( name="id.channel", joinColumns = @JoinColumn(name="chan_id") ) public class TvMagazin {@EmbeddedId public TvMagazinPk id;@Temporal(TemporalType.TIME) Date time; }@Embeddable public class TvMagazinPk implements Serializable {@ManyToOnepublic Channel channel;public String name;@ManyToOnepublic Presenter presenter; }
- 每个类一张表(Table per class)策略: 在Hibernate中对应<union-class>元素:
- 每个类层次结构一张表(Single table per class hierarchy)策略:在Hibernate中对应<subclass>元素
- 连接的子类(Joined subclasses)策略:在Hibernate中对应 <joined-subclass>元素
你可以用 @Inheritance注解来定义所选择的策略. 这个注解需要在每个类层次结构(class hierarchy) 最顶端的实体类上使用.
注意
目前还不支持在接口上进行注解.
@Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public class Flight implements Serializable {
这种策略支持双向的一对多关联. 这里不支持IDENTITY生成器策略,因为id必须在多个表间共享. 当然,一旦使用这种策略就意味着你不能使用AUTO生成器和IDENTITY生成器.
整个继承层次结构中的父类和子类的所有属性都映射到同一个表中, 他们的实例通过一个辨别符(discriminator)列来区分.:
@Entity @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="planetype",discriminatorType=DiscriminatorType.STRING ) @DiscriminatorValue("Plane") public class Plane { ... }@Entity @DiscriminatorValue("A320") public class A320 extends Plane { ... }
当每个子类映射到一个表时, @PrimaryKeyJoinColumn 和@PrimaryKeyJoinColumns 注解定义了每个子类表关联到父类表的主键:
@Entity @Inheritance(strategy=InheritanceType.JOINED) public class Boat implements Serializable { ... }@Entity public class Ferry extends Boat { ... }@Entity @PrimaryKeyJoinColumn(name="BOAT_ID") public class AmericaCupClass extends Boat { ... }
有时候通过一个(技术上或业务上)父类共享一些公共属性是很有用的, 同时还不用将该父类作为映射的实体(也就是该实体没有对应的表). 这个时候你需要使用@MappedSuperclass注解来进行映射.
@MappedSuperclass public class BaseEntity {@Basic@Temporal(TemporalType.TIMESTAMP)public Date getLastUpdate() { ... }public String getLastUpdater() { ... }... }@Entity class Order extends BaseEntity {@Id public Integer getId() { ... }... }
注意
注意,没有注解为@MappedSuperclass的父类中的属性将被忽略.
注意
除非显式使用Hibernate annotation中的@AccessType注解, 否则将从继承层次结构的根实体中继承访问类型(包括字段或方法)
注意
这对于@Embeddable对象的父类中的属性持久化同样有效. 只需要使用@MappedSuperclass注解即可 (虽然这种方式不会纳入EJB3标准)
注意
可以将处在在映射继承层次结构的中间位置的类注解为@MappedSuperclass.
注意
在继承层次结构中任何没有被注解为@MappedSuperclass 或@Entity的类都将被忽略.
你可以通过 @AttributeOverride注解覆盖实体父类中的定义的列. 这个注解只能在继承层次结构的顶端使用.
@MappedSuperclass public class FlyingObject implements Serializable {public int getAltitude() {return altitude;}@Transientpublic int getMetricAltitude() {return metricAltitude;}@ManyToOnepublic PropulsionType getPropulsion() {return metricAltitude;}... }@Entity @AttributeOverride( name="altitude", column = @Column(name="fld_altitude") ) @AssociationOverride( name="propulsion", joinColumns = @JoinColumn(name="fld_propulsion_fk") ) public class Plane extends FlyingObject {... }
在上面这个例子中,altitude属性的值最终将持久化到Plane 表的fld_altitude列.而名为propulsion的关联则保存在fld_propulsion_fk外间列.
你可以为@Entity和@MappedSuperclass注解的类 以及那些对象为@Embeddable的属性定义@AttributeOverride和@AssociationOverride.
@Entity public class Body {@Idpublic Long getId() { return id; }@OneToOne(cascade = CascadeType.ALL)@PrimaryKeyJoinColumnpublic Heart getHeart() {return heart;}... }
@Entity public class Heart {@Idpublic Long getId() { ...} }
上面的例子通过使用注解@PrimaryKeyJoinColumn定义了一对一关联.
@Entity public class Customer implements Serializable {@OneToOne(cascade = CascadeType.ALL)@JoinColumn(name="passport_fk")public Passport getPassport() {...}@Entity public class Passport implements Serializable {@OneToOne(mappedBy = "passport")public Customer getOwner() {... }
一对一关联可能是双向的.在双向关联中, 有且仅有一端是作为主体(owner)端存在的:主体端负责维护联接列(即更新). 对于不需要维护这种关系的从表则通过mappedBy属性进行声明.mappedBy的值指向主体的关联属性. 在上面这个例子中,mappedBy的值为passport. 最后,不必也不能再在被关联端(owned side)定义联接列了,因为已经在主体端进行了声明.
如果在主体没有声明@JoinColumn,系统自动进行处理: 在主表(owner table)中将创建联接列, 列名为:主体的关联属性名+下划线+被关联端的主键列名. 在上面这个例子中是passport_id, 因为Customer中关联属性名为passport,Passport的主键是id.
The third possibility (using an association table) is very exotic.
第三种方式也许是最另类的(通过关联表).
@Entity public class Customer implements Serializable {@OneToOne(cascade = CascadeType.ALL)@JoinTable(name = "CustomerPassports",joinColumns = @JoinColumn(name="customer_fk"),inverseJoinColumns = @JoinColumn(name="passport_fk"))public Passport getPassport() {...}@Entity public class Passport implements Serializable {@OneToOne(mappedBy = "passport")public Customer getOwner() {... }
Customer通过名为 CustomerPassports的关联表和Passport关联; 该关联表拥有名为passport_fk的外键列,该 外键指向Passport表,该信息定义为inverseJoinColumn的属性值, 而customer_fk外键列指向Customer表, 该信息定义为joinColumns的属性值.
这种关联可能是双向的.在双向关联中, 有且仅有一端是作为主体端存在的:主体端负责维护联接列(即更新). 对于不需要维护这种关系的从表则通过mappedBy属性进行声明.mappedBy的值指向主体的关联属性. 在上面这个例子中,mappedBy的值为passport. 最后,不必也不能再在被关联端(owned side)定义联接列了,因为已经在主体端进行了声明.
你必须明确定义关联表名和关联列名.
在实体属性一级使用@ManyToOne注解来定义多对一关联:
@Entity() public class Flight implements Serializable {@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )@JoinColumn(name="COMP_ID")public Company getCompany() {return company;}... }
@Entity() public class Flight implements Serializable {@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE}, targetEntity=CompanyImpl.class )@JoinColumn(name="COMP_ID")public Company getCompany() {return company;}... }public interface Company {...
@Entity() public class Flight implements Serializable {@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )@JoinTable(name="Flight_Company",joinColumns = @JoinColumn(name="FLIGHT_ID"),inverseJoinColumns = @JoinColumn(name="COMP_ID"))public Company getCompany() {return company;}... }
表 2.1. 集合语义
语义 | Java实现类 | 注解 |
---|---|---|
Bag 语义 | java.util.List, java.util.Collection | @org.hibernate.annotations.CollectionOfElements 或 @OneToMany 或 @ManyToMany |
List 语义 | java.util.List | (@org.hibernate.annotations.CollectionOfElements 或 @OneToMany 或 @ManyToMany) 以及 @org.hibernate.annotations.IndexColumn |
Set 语义 | java.util.Set | @org.hibernate.annotations.CollectionOfElements 或 @OneToMany 或 @ManyToMany |
Map 语义 | java.util.Map | (@org.hibernate.annotations.CollectionOfElements 或 @OneToMany 或 @ManyToMany) 以及 (空 或 @org.hibernate.annotations.MapKey/MapKeyManyToMany(支持真正的map), 或 @javax.persistence.MapKey |
从上面可以明确地看到,没有@org.hibernate.annotations.IndexColumn 注解的java.util.List集合将被看作bag类.
EJB3规范不支持原始类型,核心类型,嵌入式对象的集合.但是Hibernate对此提供了支持 (详情参考 Hibernate Annotation Extensions).
@Entity public class City {@OneToMany(mappedBy="city")@OrderBy("streetName")public List<Street> getStreets() {return streets;} ... }@Entity public class Street {public String getStreetName() {return streetName;}@ManyToOnepublic City getCity() {return city;}... }@Entity public class Software {@OneToMany(mappedBy="software")@MapKey(name="codeName")public Map<String, Version> getVersions() {return versions;} ... }@Entity @Table(name="tbl_version") public class Version {public String getCodeName() {...}@ManyToOnepublic Software getSoftware() { ... } ... }
上面这个例子中,City 中包括了以streetName排序的Street的集合. 而Software中包括了以codeName作为 key和以Version作为值的Map.
除非集合为generic类型,否则你需要指定targetEntity. 这个注解属性接受的参数为目标实体的class.
在属性级使用 @OneToMany注解可定义一对多关联.一对多关联可以是双向关联.
在EJB3规范中多对一这端几乎总是双向关联中的主体(owner)端, 而一对多这端的关联注解为@OneToMany( mappedBy=... )
@Entity public class Troop {@OneToMany(mappedBy="troop")public Set<Soldier> getSoldiers() {... }@Entity public class Soldier {@ManyToOne@JoinColumn(name="troop_fk")public Troop getTroop() {... }
Troop 通过troop 属性和Soldier建立了一对多的双向关联. 在mappedBy端不必也不能再定义任何物理映射
@Entity public class Troop {@OneToMany@JoinColumn(name="troop_fk") //we need to duplicate the physical informationpublic Set<Soldier> getSoldiers() {... }@Entity public class Soldier {@ManyToOne@JoinColumn(name="troop_fk", insertable=false, updatable=false)public Troop getTroop() {... }
通过联接表处理单向一对多关联是首选方式.这种关联通过@JoinTable注解来进行描述.
@Entity public class Trainer {@OneToMany@JoinTable(name="TrainedMonkeys",joinColumns = @JoinColumn( name="trainer_id"),inverseJoinColumns = @JoinColumn( name="monkey_id"))public Set<Monkey> getTrainedMonkeys() {... }@Entity public class Monkey {... //no bidir }
@Entity public class Employer implements Serializable {@ManyToMany(targetEntity=org.hibernate.test.metadata.manytomany.Employee.class,cascade={CascadeType.PERSIST, CascadeType.MERGE})@JoinTable(name="EMPLOYER_EMPLOYEE",joinColumns=@JoinColumn(name="EMPER_ID"),inverseJoinColumns=@JoinColumn(name="EMPEE_ID"))public Collection getEmployees() {return employees;}... }
@Entity public class Employee implements Serializable {@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE},mappedBy = "employees",targetEntity = Employer.class)public Collection getEmployers() {return employers;} }
正如前面所示,被关联端不必也不能描述物理映射: 只需要一个简单的mappedBy参数,该参数包含了主体端的属性名,这样就绑定双方的关系.
@Entity public class Store {@ManyToMany(cascade = CascadeType.PERSIST)public Set<City> getImplantedIn() {...} }@Entity public class City {... //no bidirectional relationship }
上面这个例子中,Store_Table作为联接表. Store_id列是联接到Store表的外键. 而implantedIn_id列则联接到City表.
@Entity public class Store {@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})public Set<Customer> getCustomers() {...} }@Entity public class Customer {@ManyToMany(mappedBy="customers")public Set<Store> getStores() {...} }
在上面这个例子中,Store_Customer作为联接表. stores_id列是联接到Store表的外键, 而customers_id列联接到City表.
- CascadeType.PERSIST: 如果一个实体是受管状态, 或者当persist()函数被调用时, 触发级联创建(create)操作
- CascadeType.MERGE: 如果一个实体是受管状态, 或者当merge()函数被调用时, 触发级联合并(merge)操作
- CascadeType.REMOVE: 当delete()函数被调用时, 触发级联删除(remove)操作
- CascadeType.REFRESH: 当refresh()函数被调用时, 触发级联更新(refresh)操作
- CascadeType.ALL: 以上全部
关于cascading, create/merge的语义请参考EJB3规范的6.3章节.
@Entity public class RegionalArticle implements Serializable {@Idpublic RegionalArticlePk getPk() { ... } }@Embeddable public class RegionalArticlePk implements Serializable { ... }
@Entity public class RegionalArticle implements Serializable {@EmbeddedIdpublic RegionalArticlePk getPk() { ... } }public class RegionalArticlePk implements Serializable { ... }
@Entity public class Parent implements Serializable {@Idpublic ParentPk id;public int age;@OneToMany(cascade=CascadeType.ALL)@JoinColumns ({@JoinColumn(name="parentCivility", referencedColumnName = "isMale"),@JoinColumn(name="parentLastName", referencedColumnName = "lastName"),@JoinColumn(name="parentFirstName", referencedColumnName = "firstName")})public Set<Child> children; //unidirectional... }
@Entity public class Child implements Serializable {@Id @GeneratedValuepublic Integer id;@ManyToOne@JoinColumns ({@JoinColumn(name="parentCivility", referencedColumnName = "isMale"),@JoinColumn(name="parentLastName", referencedColumnName = "lastName"),@JoinColumn(name="parentFirstName", referencedColumnName = "firstName")})public Parent parent; //unidirectional }
@Embeddable public class ParentPk implements Serializable {String firstName;String lastName;... }
javax.persistence.NamedQueries(@javax.persistence.NamedQuery(name="plane.getAll", query="select p from Plane p") ) package org.hibernate.test.annotations.query;...@Entity @NamedQuery(name="night.moreRecentThan", query="select n from Night n where n.date >= :date") public class Night {... }public class MyDao {doStuff() {Query q = s.getNamedQuery("night.moreRecentThan");q.setDate( "date", aMonthAgo );List results = q.list();...}... }
还可以通过定义 QueryHint 数组的hints 属性为查询提供一些hint信息.
表 2.2. Query hints
hint | description |
---|---|
org.hibernate.cacheable | 查询是否与二级缓存交互(默认值为false) |
org.hibernate.cacheRegion | 设置缓存区名称 (默认为otherwise) |
org.hibernate.timeout | 查询超时设定 |
org.hibernate.fetchSize | 所获取的结果集(resultset)大小 |
org.hibernate.flushMode | 本次查询所用的刷新模式 |
org.hibernate.cacheMode | 本次查询所用的缓存模式 |
org.hibernate.readOnly | 是否将本次查询所加载的实体设为只读(默认为false) |
org.hibernate.comment | 将查询注释添加入所生成的SQL |
@NamedNativeQuery(name="night&area", query="select night.id nid, night.night_duration, "+ " night.night_date, area.id aid, night.area_id, area.name "+ "from Night night, Area area where night.area_id = area.id", resultSetMapping="joinMapping") @SqlResultSetMapping(name="joinMapping", entities={@EntityResult(entityClass=org.hibernate.test.annotations.query.Night.class, fields = {@FieldResult(name="id", column="nid"),@FieldResult(name="duration", column="night_duration"),@FieldResult(name="date", column="night_date"),@FieldResult(name="area", column="area_id"),discriminatorColumn="disc"}),@EntityResult(entityClass=org.hibernate.test.annotations.query.Area.class, fields = {@FieldResult(name="id", column="aid"),@FieldResult(name="name", column="name")})} )
@Entity @SqlResultSetMapping(name="implicit", entities=@EntityResult(entityClass=org.hibernate.test.annotations.query.SpaceShip.class)) @NamedNativeQuery(name="implicitSample", query="select * from SpaceShip", resultSetMapping="implicit") public class SpaceShip {private String name;private String model;private double speed;@Idpublic String getName() {return name;}public void setName(String name) {this.name = name;}@Column(name="model_txt")public String getModel() {return model;}public void setModel(String model) {this.model = model;}public double getSpeed() {return speed;}public void setSpeed(double speed) {this.speed = speed;} }
@Entity @SqlResultSetMapping(name="compositekey",entities=@EntityResult(entityClass=org.hibernate.test.annotations.query.SpaceShip.class,fields = {@FieldResult(name="name", column = "name"),@FieldResult(name="model", column = "model"),@FieldResult(name="speed", column = "speed"), @FieldResult(name="captain.firstname", column = "firstn"),@FieldResult(name="captain.lastname", column = "lastn"),@FieldResult(name="dimensions.length", column = "length"),@FieldResult(name="dimensions.width", column = "width")}),columns = { @ColumnResult(name = "surface"),@ColumnResult(name = "volume") } )@NamedNativeQuery(name="compositekey",query="select name, model, speed, lname as lastn, fname as firstn, length, width, length * width as surface from SpaceShip", resultSetMapping="compositekey") } ) public class SpaceShip {private String name;private String model;private double speed;private Captain captain;private Dimensions dimensions;@Idpublic String getName() {return name;}public void setName(String name) {this.name = name;}@ManyToOne(fetch= FetchType.LAZY)@JoinColumns( {@JoinColumn(name="fname", referencedColumnName = "firstname"),@JoinColumn(name="lname", referencedColumnName = "lastname")} )public Captain getCaptain() {return captain;}public void setCaptain(Captain captain) {this.captain = captain;}public String getModel() {return model;}public void setModel(String model) {this.model = model;}public double getSpeed() {return speed;}public void setSpeed(double speed) {this.speed = speed;}public Dimensions getDimensions() {return dimensions;}public void setDimensions(Dimensions dimensions) {this.dimensions = dimensions;} }@Entity @IdClass(Identity.class) public class Captain implements Serializable {private String firstname;private String lastname;@Idpublic String getFirstname() {return firstname;}public void setFirstname(String firstname) {this.firstname = firstname;}@Idpublic String getLastname() {return lastname;}public void setLastname(String lastname) {this.lastname = lastname;} }
注意
观察dimension属性你会发现Hibernate支持用"."符号来表示嵌入式对象. EJB3实现不必支持这个特征,但是我们做到了:-)
如果查询返回的是单个实体,或者你打算使用系统默认的映射, 这种情况下可以不使用resultSetMapping 而是使用resultClass属性:
@NamedNativeQuery(name="implicitSample", query="select * from SpaceShip", resultClass=SpaceShip.class)
public class SpaceShip {
某些本地查询返回的是scalar值,例如报表查询. 你可以通过@ColumnResult将其映射到 @SqlResultsetMapping上. 甚至还可以在同一个本地查询的结果中混合实体和scalar类型(不过这种情况比较少见).
@SqlResultSetMapping(name="scalar", columns=@ColumnResult(name="dimension"))
@NamedNativeQuery(name="scalar", query="select length*width as dimension from SpaceShip", resultSetMapping="scalar")
本地查询中还有另外一个hint属性: org.hibernate.callable. 这个属性的布尔变量值表明这个查询是否是一个存储过程.
Hibernate 3.1 提供了多种附加的注解,这些注解可以与EJB3的实体混合/匹配使用. 他们被设计成EJB3注解的自然扩展.
为了强化EJB3的能力,Hibernate提供了与其自身特性相吻合的特殊注解. org.hibernate.annotations包已包含了所有的这些注解扩展.
你可以在EJB3规范所能提供的能力之外,就Hibernate对实体所做的一些操作进行优化.
@org.hibernate.annotations.Entity 追加了可能需要的额外的元数据, 而这些元数据超出了标准@Entity 中所定义的元数据.
- mutable: 此实体是否为可变的
- dynamicInsert: 用动态SQL新增
- dynamicUpdate: 用动态SQL更新
- selectBeforeUpdate: 指明Hibernate从不运行SQL UPDATE除非能确定对象的确已被修改
- polymorphism: (指出)实体多态是PolymorphismType.IMPLICIT(默认)还是PolymorphismType.EXPLICIT
- persister:允许对默认持久实现(persister implementation)的覆盖
- optimisticLock: 乐观锁策略(OptimisticLockType.VERSION, OptimisticLockType.NONE, OptimisticLockType.DIRTY或OptimisticLockType.ALL)
注意
@javax.persistence.Entity仍是必选的(mandatory), @org.hibernate.annotations.Entity不是取代品.
以下是一些附加的Hibernate注解扩展:
@org.hibernate.annotations.BatchSize 允许你定义批量获取该实体的实例数量(如:@BatchSize(size=4)). 当加载一特定的实体时,Hibernate将加载在持久上下文中未经初始化的同类型实体,直至批量数量(上限).
@org.hibernate.annotations.Proxy 定义了实体的延迟属性.Lazy(默认为true)定义了类是否为延迟(加载). proxyClassName是用来生成代理的接口(默认为该类本身).
@org.hibernate.annotations.Where 定义了当获取类实例时所用的SQL WHERE子句(该SQL WHERE子句为可选).
@org.hibernate.annotations.Check 定义了在DDL语句中定义的合法性检查约束(该约束为可选).
@OnDelete(action=OnDeleteAction.CASCADE) 定义于被连接的子类(joined subclass):在删除时使用SQL级连删除,而非通常的Hibernate删除机制.
@Table(name="tableName", indexes = { @Index(name="index1", columnNames={"column1", "column2"} ) } ) 在tableName表的列上创建定义好的索引. 该注解可以被应用于关键表或者是其他次要的表.@Tables 注解允许你在不同的表上应用索引. 此注解预期在使用@javax.persistence.Table或@javax.persistence.SecondaryTable的地方中出现.
注意
@org.hibernate.annotations.Table 是对 @javax.persistence.Table的补充而不是它的替代品. 特别是当你打算改变表名的默认值的时候,你必须使用@javax.persistence.Table, 而不是@org.hibernate.annotations.Table.
@Entity @BatchSize(size=5) @org.hibernate.annotations.Entity(selectBeforeUpdate = true,dynamicInsert = true, dynamicUpdate = true,optimisticLock = OptimisticLockType.ALL,polymorphism = PolymorphismType.EXPLICIT) @Where(clause="1=1") @org.hibernate.annotations.Table(name="Forest", indexes = { @Index(name="idx", columnNames = { "name", "length" } ) } ) public class Forest { ... }
@Entity @Inheritance(strategy=InheritanceType.JOINED ) public class Vegetable { ... }@Entity @OnDelete(action=OnDeleteAction.CASCADE) public class Carrot extends Vegetable { ... }
@org.hibernate.annotations.GenericGenerator 允许你定义一个Hibernate特定的id生成器.
@Id @GeneratedValue(generator="system-uuid") @GenericGenerator(name="system-uuid", strategy = "uuid") public String getId() {@Id @GeneratedValue(generator="hibseq") @GenericGenerator(name="hibseq", strategy = "seqhilo",parameters = {@Parameter(name="max_lo", value = "5"),@Parameter(name="sequence", value="heybabyhey")} ) public Integer getId() {
strategy可以是Hibernate3生成器策略的简称, 或者是一个IdentifierGenerator实现的(带包路径的)全限定类名. 你可以通过parameters属性增加一些参数.
访问类型是根据@Id或@EmbeddedId 在实体继承层次中所处的位置推演而得的.子实体(Sub-entities), 内嵌对象和被映射的父类均继承了根实体(root entity)的访问类型.
使用定制的访问类型策略
优化类级或属性级的访问类型
为支持这种行为,Hibernate引入了@AccessType注解.你可以对以下元素定义访问类型:
实体
父类
可内嵌的对象
属性
被注解元素的访问类型会被覆盖,若覆盖是在类一级上,则所有的属性继承访问类型. 对于根实体,其访问类型会被认为是整个继承层次中的缺省设置(可在类或属性一级覆盖).
若访问类型被标以"property",则Hibernate会扫描getter方法的注解,若访问类型被标以"field", 则扫描字段的注解.否则,扫描标为@Id或@embeddedId的元素.
你可以覆盖某个属性(property)的访问类型,但是受注解的元素将不受影响: 例如一个具有field访问类型的实体,(我们)可以将某个字段标注为 @AccessType("property"), 则该字段的访问类型随之将成为property,但是其他字段上依然需要携带注解.
若父类或可内嵌的对象没有被注解,则使用根实体的访问类型(即使已经在非直系父类或可内嵌对象上定义了访问类型). 此时俄罗斯套娃(Russian doll)原理就不再适用.(译注:俄罗斯套娃(матрёшка或 матрешка)是俄罗斯特产木制玩具, 一般由多个一样图案的空心木娃娃一个套一个组成,最多可达十多个,通常为圆柱形,底部平坦可以直立.)
@Entity
public class Person implements Serializable {@Id @GeneratedValue //access type fieldInteger id;@Embedded@AttributeOverrides({@AttributeOverride(name = "iso2", column = @Column(name = "bornIso2")),@AttributeOverride(name = "name", column = @Column(name = "bornCountryName"))})Country bornIn;
}@Embeddable
@AccessType("property") //override access type for all properties in Country
public class Country implements Serializable {private String iso2;private String name;public String getIso2() {return iso2;}public void setIso2(String iso2) {this.iso2 = iso2;}@Column(name = "countryName")public String getName() {return name;}public void setName(String name) {this.name = name;}
}
有时候,你想让数据库,而非JVM,来替你完成一些计算,也可能想创建某种虚拟列. 你可以使用SQL片段(亦称为公式),而不是将属性映射到(物理)列. 这种属性是只读的(属性值由公求得).
@Formula("obj_length * obj_height * obj_width")public long getObjectVolume()
@TypeDefs({@TypeDef(name="caster",typeClass = CasterStringType.class,parameters = {@Parameter(name="cast", value="lower")})} ) package org.hibernate.test.annotations.entity;... public class Forest {@Type(type="caster")public String getSmallText() {... }
当使用组合的用户自定义类型时,你必须自己表示列的定义. @Columns就是为了此目的而引入的.
@Type(type="org.hibernate.test.annotations.entity.MonetaryAmountUserType") @Columns(columns = {@Column(name="r_amount"),@Column(name="r_currency") }) public MonetaryAmount getAmount() {return amount; }public class MonetaryAmount implements Serializable {private BigDecimal amount;private Currency currency;... }
通过在列属性(property)上使用@Index注解, 可以在特定列上定义索引,columnNames属性(attribute)将随之被忽略.
@Column(secondaryTable="Cat1") @Index(name="story1index") public String getStoryPart1() {return storyPart1; }
在嵌入式对象内部,你可以在那些指向该嵌入式对象所属元素的属性上定义该注解.
@Entity public class Person {@Embeddable public Address address;... }@Embeddable public class Address {@Parent public Person owner;... }person == person.address.owner
某些属性可以在对数据库做插入或更新操作的时候生成. Hibernate能够处理这样的属性,并触发一个后续的查询来读取这些属性.
@Entity public class Antenna {@Id public Integer id;@Generated(GenerationTime.ALWAYS) @Column(insertable = false, updatable = false)public String longitude;@Generated(GenerationTime.INSERT) @Column(insertable = false)public String latitude; }
你可以将属性注解为@Generated. 但是你要注意insertability和updatability不要和你选择的生成策略冲突. 如果选择了GenerationTime.INSERT,该属性不能包含insertable列, 如果选择了GenerationTime.ALWAYS,该属性不能包含insertable和updatable列.
@Version属性不可以为 @Generated(INSERT)(设计时), 只能是NEVER或ALWAYS.
@Entity public class Child {...@ManyToOne@NotFound(action=NotFoundAction.IGNORE)public Parent getParent() { ... }... }
@Entity public class Child {...@ManyToOne@OnDelete(action=OnDeleteAction.CASCADE)public Parent getParent() { ... }... }
上面这个例子中,Hibernate将生成一个数据库级的级联删除约束.
EJB3为延迟加载和获取模式提供了fetch选项,而Hibernate 这方面提供了更丰富的选项集.为了更好的调整延迟加载和获取策略,Hibernate引入了 一些附加的注解:
@LazyToOne: 定义了 @ManyToOne 和@OneToOne 关联的延迟选项.LazyToOneOption 可以是PROXY (例如:基于代理的延迟加载),NO_PROXY (例如:基于字节码增强的延迟加载 - 注意需要在构建期处理字节码) 和FALSE (非延迟加载的关联)
@LazyCollection: 定义了 @ManyToMany和@OneToMany 关联的延迟选项. LazyCollectionOption 可以是TRUE (集合具有延迟性,只有在访问的时候才加载),EXTRA (集合具有延迟性,并且所有的操作都会尽量避免加载集合, 对于一个巨大的集合特别有用,因为这样的集合中的元素没有必要全部加载)和 FALSE (非延迟加载的关联)
@Fetch: 定义了加载关联关系的获取策略. FetchMode 可以是SELECT (在需要加载关联的时候触发select操作),SUBSELECT (只对集合有效,使用了子查询策略,详情参考Hibernate参考文档) orJOIN (在加载主实体(owner entity)的时候使用SQL JOIN来加载关联关系).JOIN 将覆写任何延迟属性 (通过JOIN策略加载的关联将不再具有延迟性).
The Hibernate annotations overrides the EJB3 fetching options.
Hibernate注解覆写了EJB3提供的获取(fetch)选项.
表 2.3. 延迟和获取选项的等效注解
Annotations | Lazy | Fetch |
---|---|---|
@[One|Many]ToOne](fetch=FetchType.LAZY) | @LazyToOne(PROXY) | @Fetch(SELECT) |
@[One|Many]ToOne](fetch=FetchType.EAGER) | @LazyToOne(FALSE) | @Fetch(JOIN) |
@ManyTo[One|Many](fetch=FetchType.LAZY) | @LazyCollection(TRUE) | @Fetch(SELECT) |
@ManyTo[One|Many](fetch=FetchType.EAGER) | @LazyCollection(FALSE) | @Fetch(JOIN) |
- 用@BatchSizebatch设置集合的batch大小
- 用@Where注解设置Where子句
- 用注解@Check来设置check子句
- 用注解@OrderBy来设置SQL的order by子句
- 利用@OnDelete(action=OnDeleteAction.CASCADE) 注解设置级连删除策略
你也可以利用@Sort注解定义一个排序比较器(sort comparator), 表明希望的比较器类型,无序、自然顺序或自定义排序,三者择一.若你想用你自己实现的comparator, 你还需要利用comparator属性(attribute)指明实现类.
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)@JoinColumn(name="CUST_ID")@Sort(type = SortType.COMPARATOR, comparator = TicketComparator.class)@Where(clause="1=1")@OnDelete(action=OnDeleteAction.CASCADE)public SortedSet<Ticket> getTickets() {return tickets;}
关于这些注解更详细的信息,请参阅此前的描述.
@OneToMany(cascade = CascadeType.ALL) @IndexColumn(name = "drawer_position", base=1) public List<Drawer> getDrawers() {return drawers; }
注意
假如你忘了设置@IndexColumn, Hibernate会采用包(bag)语义(译注:即允许重复元素的无序集合).
Hibernate注解支持true Map映射, 如果没有设置@javax.persistence.MapKey, hibernate将key元素或嵌入式对象直接映射到他们所属的列. 要覆写默认的列,可以使用以下注解:@org.hibernate.annotations.MapKey适用的key为基本类型或者嵌入式对象,@org.hibernate.annotations.MapKey适用的key为实体.
Hibernate Annotations还支持核心类型集合(Integer, String, Enums, ......)、 可内嵌对象的集合,甚至基本类型数组.这就是所谓的元素集合.
元素集合可用@CollectionOfElements来注解(作为@OneToMany的替代). 为了定义集合表(译注:即存放集合元素的表,与下面提到的主表对应),要在关联属性上使用@JoinTable注解, joinColumns定义了介乎实体主表与集合表之间的连接字段(inverseJoincolumn是无效的且其值应为空). 对于核心类型的集合或基本类型数组,你可以在关联属性上用@Column来覆盖存放元素值的字段的定义. 你还可以用@AttributeOverride来覆盖存放可内嵌对象的字段的定义. 要访问集合元素,需要将该注解的name属性值设置为"element"("element"用于核心类型,而"element.serial" 用于嵌入式对象的serial属性).要访问集合的index/key,则将该注解的name属性值设置为"key".
@Entity public class Boy {private Integer id;private Set<String> nickNames = new HashSet<String>();private int[] favoriteNumbers;private Set<Toy> favoriteToys = new HashSet<Toy>();private Set<Character> characters = new HashSet<Character>();@Id @GeneratedValuepublic Integer getId() {return id;}@CollectionOfElementspublic Set<String> getNickNames() {return nickNames;}@CollectionOfElements@JoinTable(table=@Table(name="BoyFavoriteNumbers"),joinColumns = @JoinColumn(name="BoyId"))@Column(name="favoriteNumber", nullable=false)@IndexColumn(name="nbr_index")public int[] getFavoriteNumbers() {return favoriteNumbers;}@CollectionOfElements@AttributeOverride( name="element.serial", column=@Column(name="serial_nbr") )public Set<Toy> getFavoriteToys() {return favoriteToys;}@CollectionOfElementspublic Set<Character> getCharacters() {return characters;}... }public enum Character {GENTLE,NORMAL,AGGRESSIVE,ATTENTIVE,VIOLENT,CRAFTY }@Embeddable public class Toy {public String name;public String serial;public Boy owner;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSerial() {return serial;}public void setSerial(String serial) {this.serial = serial;}@Parentpublic Boy getOwner() {return owner;}public void setOwner(Boy owner) {this.owner = owner;}public boolean equals(Object o) {if ( this == o ) return true;if ( o == null || getClass() != o.getClass() ) return false;final Toy toy = (Toy) o;if ( !name.equals( toy.name ) ) return false;if ( !serial.equals( toy.serial ) ) return false;return true;}public int hashCode() {int result;result = name.hashCode();result = 29 * result + serial.hashCode();return result;} }
在嵌入式对象的集合中,可以使用 @Parent注解嵌入式对象的某属性. 该属性指向该嵌入式对象所属的集合实体.
注意
旧版的Hibernate Annotations用@OneToMany来标识集合元素. 由于语义矛盾,我们引入了@CollectionOfElements注解. 用@OneToMany来标识集合元素的这种旧有方式目前尚有效, 但是不推荐使用,而且在以后的发布版本中不再支持这种方式.
为了优化数据库访问,你可以激活所谓的Hibernate二级缓存.该缓存是可以按每个实体和集合进行配置的.
@org.hibernate.annotations.Cache定义了缓存策略及给定的二级缓存的范围. 此注解适用于根实体(非子实体),还有集合.
@Entity @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) public class Forest { ... }
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)@JoinColumn(name="CUST_ID")@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)public SortedSet<Ticket> getTickets() {return tickets;}
@Cache(CacheConcurrencyStrategy usage(); (1)String region() default ""; (2)String include() default "all"; (3) )
(1) |
usage: 给定缓存的并发策略(NONE, READ_ONLY, NONSTRICT_READ_WRITE, READ_WRITE, TRANSACTIONAL) |
(2) |
region (可选的):缓存范围(默认为类的全限定类名或是集合的全限定角色名) |
(3) |
include (可选的):值为all时包括了所有的属性(proterty), 为non-lazy时仅含非延迟属性(默认值为all) |
Hibernate具有数据过滤器的概念,可在运行期应用于一个给定的session.过滤器需要事先定义好.
现在我们来定义应用于实体或集合加载时的SQL过滤器子句.我们使用@Filter,并将其置于实体或集合元素上.
@Entity @FilterDef(name="minLength", parameters=@ParamDef( name="minLength", type="integer" ) ) @Filters( {@Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length"),@Filter(name="minLength", condition=":minLength <= length") } ) public class Forest { ... }
flushMode: 定义查询的刷新模式(Always, Auto, Commit或Manual)
cacheable: 查询该不该被缓存
cacheRegion: 若查询已被缓存时所用缓存的范围
fetchSize: 针对该查询的JDBC statement单次获取记录的数目
timeout: 查询超时
callable: 仅用于本地化查询(native query),对于存储过程设为true
comment: 一旦激活注释功能,我们会在向数据库交送查询请求时看到注释
cacheMode: 缓存交护模式(get, ignore,normal,或refresh)
readOnly: 不管是否从查询获取元素都是在只读模式下
注意,EJB3已公开的最终草案中引入了@QueryHint的概念, 这可能是定义hints更好的方法.
上一页 | 上一级 | 下一页 |
第 1 章 创建一个注解项目 | 起始页 | 第 3 章 通过XML覆写元数据 |
Hibernate注解完整文档相关推荐
- Spring 2.5 jar 所有开发包及完整文档及项目开发实例
Spring 2.5 jar 所有开发包及完整文档及项目开发实例 spring jar 包详解spring.jar是包含有完整发布的单个jar包,spring.jar中包含除了 spring-mock ...
- openlayers5之完整文档v5.1.3.zip下载
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/gisdoer/article/details/81697967 openlayers完整文档v5.1 ...
- 大型三甲医院云HIS系统源码 强大的电子病历+完整文档
医院HIS系统源码云HIS系统:SaaS运维平台+多医院入驻+强大的电子病历+完整文档 有源码,有演示 一.系统概述 Ø 采用主流成熟技术,软件结构简洁.代码规范易阅读,SaaS应用,全浏览器访问前后 ...
- 【源码分享】ASP.NET大型快运(快递)管理系统带完整文档
ASP.NET大型快运(快递)管理系统源码带完整文档 大型快递(快运)管理系统基于webservice的分布式系统,集成快递物流业务流程管理,快递物流公司内部管理,快递物流单全网跟踪监控,并集成基于w ...
- (运动会模拟射靶)Python期末大作业(附完整文档)
python期末大作业,因为是数据分析与可视化,所以我在网上搜索的时候,找到了一篇关于大作业射靶的题目,原文写的很好,也提供了下载,本篇在其基础上增加了数据可视化-图表,并且增加了,方差,个人平均分在 ...
- ASP.NET大型快运(快递)ERP系统带完整文档【源码分享】
ASP.NET大型快运(快递)管理系统源码带完整文档 源码分享,需要源码学习参考可私信我. 大型快递(快运)管理系统基于webservice的分布式系统,集成快递物流业务流程管理,快递物流公司内部管理 ...
- 敏感词“智能”检测(代码和完整文档在最后)
完整文档下载 案例代码下载
- Hibernate中文参考文档(JFIS)
HIBERNATE - 符合Java习惯的关系数据库持久化 下一页 HIBERNATE - 符合Java习惯的关系数据库持久化 Hibernate参考文档 3.0.4 目录 前言 1. 翻译 ...
- 完整版H5社交聊天平台源码[完整数据库+完整文档教程]
介绍: 开发语言:PHP 数据库:MySQL 完整版H5社交聊天平台源码:一款优化了的版本,带有完善的文档教程和完整数据库文件. 东西非常的齐全,这款是客户定制的,所以东西很完整,也值不少钱,源码开发 ...
最新文章
- python列表做参数传值_python不定参数传值怎么做-问答-阿里云开发者社区-阿里云...
- 51nod 1513-3的幂的和(费马小定理+快速幂)
- Android之ListView原理学习与优化总结
- 为什么python打不开_python文件打不开如何解决
- 用 Python 写出这样的进度条,刷新了我对进度条的认知
- VMware pro 14
- 微信公众号从测试号迁移到正式号
- mysql链接is not allow_解决Mysql远程连接出错不允许访问 ERROR 1130:Host is not allow...
- shell 脚本中双引号、单引号、反引号的区别
- 关闭git命令窗快捷键_zsh中git的快捷命令
- 字节跳动内推 校招 社招 实习生都可以投递
- 南京沁恒推出的国产 M3 架构芯片与意法半导体 M3 芯片对比
- 【转载】一个程序猿必须会玩的游戏
- 渐变折射率(GRIN)多模光纤的建模
- erdas叠加显示_ERDAS影像融合
- xcode8 cocoapods 执行 pod spec lint 报错
- Centos 6安装Maven
- selenium+unittest自动化测试发送邮件
- 我的京东管理生涯随想 1
- 数美科技斩获36氪“WISE 2020数据智能最佳解决方案”
热门文章
- WINDOWS 服务器终极安全方案
- 7-41 成绩统计分析表(*) (10 分)
- lumia 525 android 7.1,真神机!Lumia 520 吃上 Android 7.1
- Linux网络:HTTP协议 | URL | 协议格式 | HTTP方法 | HTTP状态码 | Cookie与Session
- U3D各向异性Shader
- Suflo-Cyanine5.5 Tyramide,Suflo-Cyanine3 TSA,磺酸基-花青素Cy5 酪酰胺
- 用好大数据须有大智慧
- QQ这个永久下线的功能,又起死回生了
- 在抽签仪式上,如何通过软件系统的随机抽签功能来决定选手的比赛出场顺序?
- 高端骨传导耳机来袭,最强旗舰机南卡Runner Pro4测评!