@Column

@Column标记表示所持久化属性所映射表中的字段,该注释的属性定义如下:

@Target({METHOD, FIELD}) @Retention(RUNTIME)

public @interface Column {

String name() default "";

boolean unique() default false;

boolean nullable() default true;

boolean insertable() default true;

boolean updatable() default true;

String columnDefinition() default "";

String table() default "";

int length() default 255;

int precision() default 0;

int scale() default 0;

}

在使用此@Column标记时,需要注意以下几个问题:

l 此标记可以标注在getter方法或属性前,例如以下的两种标注方法都是正确的:

标注在属性前:

@Entity

@Table(name = "contact")

public class ContactEO{

@Column(name=" contact_name ")

private String name;

}

标注在getter方法前:

@Entity

@Table(name = "contact")

public class ContactEO{

@Column(name=" contact_name ")

public String getName() {

return name;

}

}

提示:JPA规范中并没有明确指定那种标注方法,只要两种标注方式任选其一都可以。这根据个人的喜好来选择,笔者习惯使用第二种方法。

l unique属性表示该字段是否为唯一标识,默认为false。如果表中有一个字段需要唯一标识,则既可以使用该标记,也可以使用@Table标记中的@UniqueConstraint

l nullable属性表示该字段是否可以为null值,默认为true

l insertable属性表示在使用“INSERT”脚本插入数据时,是否需要插入该字段的值。

l updatable属性表示在使用“UPDATE”脚本插入数据时,是否需要更新该字段的值。insertableupdatable属性一般多用于只读的属性,例如主键和外键等。这些字段的值通常是自动生成的。

l columnDefinition属性表示创建表时,该字段创建的SQL语句,一般用于通过Entity生成表定义时使用。

l table属性表示当映射多个表时,指定表的表中的字段。默认值为主表的表名。有关多个表的映射将在本章的5.6小节中详细讲述。

l length属性表示字段的长度,当字段的类型为varchar时,该属性才有效,默认为255个字符。

l precision属性和scale属性表示精度,当字段类型为double时,precision表示数值的总长度,scale表示小数点所占的位数。

下面举几个小例子:

示例一:指定字段“contact_name”的长度是“512”,并且值不能为null

private String name;

@Column(name="contact_name",nullable=false,length=512)

public String getName() {

return name;

}

创建的SQL语句如下所示。

CREATE TABLE contact (

id integer not null,

contact_name varchar (512) not null,

primary key (id)

)

示例二:指定字段“monthly_income”月收入的类型为double型,精度为12位,小数点位数为2位。

private BigDecimal monthlyIncome;

@Column(name="monthly_income",precision=12, scale=2)

public BigDecimal getMonthlyIncome() {

return monthlyIncome;

}

创建的SQL语句如下所示。

CREATE TABLE contact (

id integer not null,

monthly_income double(12,2),

primary key (id)

)

示例三:自定义生成CLOB类型字段的SQL语句。

private String name;

@Column(name=" contact_name ",columnDefinition="clob not null")

public String getName() {

return name;

}

生成表的定义SQL语句如下所示。

CREATE TABLE contact (

id integer not null,

contact_name clob (200) not null,

primary key (id)

)

其中,加粗的部分为columnDefinition属性设置的值。若不指定该属性,通常使用默认的类型建表,若此时需要自定义建表的类型时,可在该属性中设置。

提示:通过Entity定义生成表,还是通过表配置Entity,这两种ORM的策略。有关两种方法的映射策略好坏,将在本书的章节中“JPA工具的使用”一章进行详细的比较。

示例四:字段值为只读的,不允许插入和修改。通常用于主键和外键。

private Integer id;

@Column(name="id",insertable=false,updatable=false)

public Integer getId() {

return id;

}

分析代码作用?提示和mysql迁移有关?@NamedQueries(@NamedQuery(name = "findAlp3DHoleDetailLayer", query = "SELECT min(o.createDate) FROM Alp3DHoleDetailLayer o ")) @Entity @Table(name = "Alp3DHoleDetailLayer", schema = "dbo", catalog = "[LKM CAPL]") public class Alp3DHoleDetailLayer implements Serializable { private static final long serialVersionUID = 5221852140544785735L; @Id @Column(name = "HoleLayerId") private int holeLayerId; @Column(name = "WorkId") private String workId; @Column(name = "HoleTypeId") private int holeTypeId; @Column(name = "HoleID") private int holeId; @Column(name = "Hole_Dir") private String holeDir; @Column(name = "FaceType") private String faceType; @Column(name = "Hole_X") private float holeX; @Column(name = "Hole_Y") private float holeY; @Column(name = "Hole_Z") private float holeZ; @Column(name = "Hole_radiusSmall") private float holeRadiusSmall; @Column(name = "Hole_radius") private float holeRadius; @Column(name = "Hole_dept") private float holeDept; @Column(name = "Hole_rAngle") private float holeRAngle; @Column(name = "Hole_bevelingA1") private String holeBevelingA1; @Column(name = "Hole_bevelingA2") private String holeBevelingA2; @Column(name = "HandId") private String handId; @Column(name = "layout") private int layout; @Column(name = "oneInterfere") private float oneInterfere; @Column(name = "TwoInterfere") private float twoInterfere; @Column(name = "isSideIntersect") private Boolean isSideIntersect; @Column(name = "BackFrameDept") private float backFrameDept; @Column(name = "beginpoint") private float beginpoint; @Column(name = "oDa") private String oDa; @Column(name = "CreateDate") private Date createDate; @Column(name = "CreateBy") private String createBy; @Column(name = "isTong") private Boolean isTong; @Column(name = "addedDeep") private float addedDeep; @Column(name = "isFlat") private Boolean isFlat; @Column(name = "error") private String error; @Column(name = "waterBlockPoint") private float waterBlockPoint; @Column(name = "waterBlockPointEnd") private float waterBlockPointEnd;
最新发布
07-26
<think>我们有一个实体类 `Alp3DHoleDetailLayer`,它映射到数据库中的表 `Alp3DHoleDetailLayer`(位于模式 `dbo` 和目录 `[LKM CAPL]`)。这个类包含多个字段,每个字段都通过 `@Column` 注解映射到表的列。 在类的顶部,有一个 `@NamedQueries` 注解,其中定义了一个名为 `findAlp3DHoleDetailLayer` 的命名查询。这个查询的目的是从 `Alp3DHoleDetailLayer` 表中获取最小的 `createDate` 值。 ### 代码分析 1. **实体类映射**: - `@Entity`:表示该类是一个JPA实体。 - `@Table`:指定了数据库表名、模式(schema)和目录(catalog)。注意,目录名 `[LKM CAPL]` 使用了方括号,这可能是为了处理数据库标识符中的空格(在SQL Server中常见)。 2. **命名查询**: - `@NamedQuery(name = "findAlp3DHoleDetailLayer", query = "SELECT min(o.createDate) FROM Alp3DHoleDetailLayer o")` - 这个查询的作用是获取整个表中 `createDate` 字段的最小值(即最早的创建日期)。 3. **字段映射**: - 类中定义了多个字段,每个字段对应表中的一个列。例如: - `holeLayerId` 是主键(使用 `@Id` 注解)。 - 其他字段如 `workId`、`holeTypeId` 等都有对应的列。 - 注意:有些字段使用了基本类型(如 `int`, `float`),有些使用了包装类型(如 `Boolean`),还有字符串(`String`)和日期(`Date`)。 ### 与MySQL迁移的关系 这个实体类目前是针对SQL Server数据库设计的(因为使用了 `dbo` 模式和方括号括起来的目录名)。如果要迁移到MySQL,需要注意以下几点: 1. **表名和模式(Schema)**: - 在MySQL中,通常没有目录(catalog)的概念,模式(schema)在MySQL中相当于数据库名。 - 需要调整 `@Table` 注解: - 移除 `catalog`(或者将其设置为MySQL的数据库名,但通常不需要)。 - `schema` 可以保留,但MySQL中的模式等同于数据库名,因此如果迁移后的数据库名不同,需要修改。 例如,如果迁移后的数据库名为 `lkm_capl`,则可以修改为: ```java @Table(name = "Alp3DHoleDetailLayer", schema = "lkm_capl") ``` 2. **字段类型映射**: - 在SQL Server中,`float` 类型对应Java的`float`,在MySQL中通常使用`FLOAT`或`DOUBLE`。 - 日期类型:SQL Server的`datetime`对应Java的`Date`,在MySQL中同样使用`DATETIME`或`TIMESTAMP`。 - 布尔类型:在SQL Server中使用`BIT`,在MySQL中可以使用`TINYINT(1)`或`BOOLEAN`(实际上是`TINYINT(1)`的别名)。 3. **命名策略**: - SQL Server通常使用大写字母开头的列名,而MySQL默认是大小写敏感的(取决于文件系统),建议在迁移后统一使用小写或大写,并保持一致。 - 在MySQL中,表名和列名默认是大小写敏感的(取决于系统设置),但通常我们使用小写字母和下划线分隔。 4. **转义字符**: - 在SQL Server中,如果标识符包含空格或特殊字符,可以使用方括号(如 `[LKM CAPL]`)。在MySQL中,使用反引号(\`)来转义标识符。但在实体类中,我们不需要在注解里写转义字符,除非表名或列名是MySQL的保留字。例如,如果表名在MySQL中是保留字,则可以这样写: ```java @Table(name = "`Alp3DHoleDetailLayer`", schema = "lkm_capl") ``` 5. **查询兼容性**: - 命名查询 `SELECT min(o.createDate) FROM Alp3DHoleDetailLayer o` 在MySQL中同样有效,因为这是标准的JPQL(也对应标准的SQL聚合函数)。但是,如果表名或列名在MySQL中是保留字,则需要在JPQL中避免使用保留字,或者使用转义(但JPQL不支持转义,所以最好避免使用保留字作为标识符)。 ### 迁移建议步骤 1. **创建MySQL数据库和表**: - 根据实体类设计,在MySQL中创建相应的表。注意修改列的数据类型以匹配MySQL(例如,将 `BIT` 改为 `TINYINT(1)` 表示布尔值)。 2. **修改实体类注解**: - 更新 `@Table` 注解的 `catalog` 和 `schema` 属性,使其指向MySQL的数据库名(作为schema)。 3. **调整数据类型**: - 检查每个字段的类型是否在MySQL中有合适的对应。例如,在MySQL中,`float` 类型可能精度不够,考虑使用 `DOUBLE`。 4. **测试查询**: - 确保命名查询在MySQL中能够正确执行。 ### 示例:修改后的实体类(MySQL版本) ```java @Entity @Table(name = "alp_3d_hole_detail_layer", schema = "lkm_capl") // 在MySQL中,表名通常小写,使用下划线分隔 @NamedQueries({ @NamedQuery(name = "findAlp3DHoleDetailLayer", query = "SELECT min(o.createDate) FROM Alp3DHoleDetailLayer o") }) public class Alp3DHoleDetailLayer implements Serializable { private static final long serialVersionUID = 5221852140544785735L; @Id @Column(name = "hole_layer_id") private int holeLayerId; @Column(name = "work_id") private String workId; // 其他字段的注解也要相应修改列名,使用小写和下划线 // 例如: // @Column(name = "hole_type_id") // private int holeTypeId; // 注意:布尔类型在MySQL中映射为TINYINT(1) @Column(name = "is_side_intersect") private Boolean isSideIntersect; // 其他字段... } ``` ### 注意事项 - **表名和列名大小写**:在MySQL中,表名和列名的大小写敏感取决于操作系统的文件系统(Linux区分,Windows不区分)和MySQL的配置(`lower_case_table_names`)。为了可移植性,建议统一使用小写字母和下划线命名。 - **保留字**:避免使用MySQL的保留字作为表名或列名。如果必须使用,在SQL语句中需要用反引号转义,但在JPA的`@Column`和`@Table`的`name`属性中,JPA提供方(如Hibernate)会自动处理(通常会在生成SQL时转义)。 - **数据迁移**:将数据从SQL Server迁移到MySQL时,可以使用工具如SQL Server Management Studio的导出功能,或者使用ETL工具(如Pentaho Data Integration)进行转换和迁移。 ### 总结 这段代码定义了一个JPA实体类,用于映射数据库中的表。命名查询用于获取表中最早创建日期。迁移到MySQL时,主要注意表名、列名的大小写和转义,以及数据类型的匹配。同时,需要调整实体类中的`@Table`注解,去掉`catalog`并修改`schema`为MySQL的数据库名,并确保列名在MySQL中有效。 §§相关问题§§: 1. 在JPA中,如何定义和使用命名查询(NamedQuery)? 2. 将SQL Server数据库迁移到MySQL时,有哪些常见的数据类型映射问题? 3. 如何配置JPA(如Hibernate)以连接MySQL数据库? 4. 在实体类中,基本类型(如int)和包装类型(如Integer)在使用上有何区别?</think># JPA实体类与命名查询分析 ## 代码作用分析 这个`Alp3DHoleDetailLayer`类是一个JPA实体类,用于映射数据库中的"Alp3DHoleDetailLayer"表。特别是其中的命名查询`findAlp3DHoleDetailLayer`具有特殊作用,与MySQL迁移有密切关系。 ### 核心功能分析 ```java @NamedQueries(@NamedQuery(name = "findAlp3DHoleDetailLayer", query = "SELECT min(o.createDate) FROM Alp3DHoleDetailLayer o ")) ``` 这个命名查询的作用是**获取表中最早创建的记录的时间戳**。在数据库迁移场景中,这种查询通常用于: 1. **数据迁移监控**:确定迁移的起始点 2. **增量迁移**:识别最早需要迁移的记录 3. **数据验证**:比较源和目标数据库的最早记录时间 4. **迁移进度跟踪**:作为迁移进度的基准点 ### 完整实体类解析 ```java @Entity @Table(name = "Alp3DHoleDetailLayer", schema = "dbo", catalog = "[LKM CAPL]") public class Alp3DHoleDetailLayer implements Serializable { // 序列化ID - 用于对象序列化 private static final long serialVersionUID = 5221852140544785735L; // 主键字段 @Id @Column(name = "HoleLayerId") private int holeLayerId; // 工作标识字段 @Column(name = "WorkId") private String workId; // 孔洞类型ID @Column(name = "HoleTypeId") private int holeTypeId; // 孔洞ID @Column(name = "HoleID") private int holeId; // 孔洞方向 @Column(name = "Hole_Dir") private String holeDir; // 表面类型 @Column(name = "FaceType") private String faceType; // 三维坐标 @Column(name = "Hole_X") private float holeX; @Column(name = "Hole_Y") private float holeY; @Column(name = "Hole_Z") private float holeZ; // 孔洞半径 @Column(name = "Hole_radiusSmall") private float holeRadiusSmall; @Column(name = "Hole_radius") private float holeRadius; // 孔洞深度 @Column(name = "Hole_dept") private float holeDept; // 角度参数 @Column(name = "Hole_rAngle") private float holeRAngle; // 斜面参数 @Column(name = "Hole_bevelingA1") private String holeBevelingA1; @Column(name = "Hole_bevelingA2") private String holeBevelingA2; // 操作标识 @Column(name = "HandId") private String handId; // 布局信息 @Column(name = "layout") private int layout; // 干涉参数 @Column(name = "oneInterfere") private float oneInterfere; @Column(name = "TwoInterfere") private float twoInterfere; // 侧面干涉标志 @Column(name = "isSideIntersect") private Boolean isSideIntersect; // 框架深度 @Column(name = "BackFrameDept") private float backFrameDept; // 起点位置 @Column(name = "beginpoint") private float beginpoint; // 其他参数 @Column(name = "oDa") private String oDa; // 创建信息 - 迁移关键字段 @Column(name = "CreateDate") private Date createDate; @Column(name = "CreateBy") private String createBy; // 状态标志 @Column(name = "isTong") private Boolean isTong; // 附加深度 @Column(name = "addedDeep") private float addedDeep; // 平面标志 @Column(name = "isFlat") private Boolean isFlat; // 错误信息 @Column(name = "error") private String error; // 防水点位置 @Column(name = "waterBlockPoint") private float waterBlockPoint; @Column(name = "waterBlockPointEnd") private float waterBlockPointEnd; } ``` ## MySQL迁移相关实现 ### 1. 迁移服务类实现 ```java @Stateless public class MigrationService { private static final Logger logger = Logger.getLogger(MigrationService.class); @PersistenceContext(unitName = "source-db") private EntityManager sourceEm; @PersistenceContext(unitName = "target-db") private EntityManager targetEm; /** * 获取最早创建日期 - 用于确定迁移起点 */ public Date getEarliestCreateDate() { try { // 使用命名查询获取最早创建日期 Query query = sourceEm.createNamedQuery("findAlp3DHoleDetailLayer"); return (Date) query.getSingleResult(); } catch (NoResultException e) { logger.warning("未找到任何记录"); return null; } } /** * 执行增量迁移 * @param lastMigrationDate 上次迁移的最后日期 */ public int migrateData(Date lastMigrationDate) { // 构建查询获取需要迁移的记录 String jpql = "SELECT o FROM Alp3DHoleDetailLayer o WHERE o.createDate > :lastDate ORDER BY o.createDate"; List<Alp3DHoleDetailLayer> records = sourceEm.createQuery(jpql, Alp3DHoleDetailLayer.class) .setParameter("lastDate", lastMigrationDate) .getResultList(); int count = 0; for (Alp3DHoleDetailLayer record : records) { // 在目标数据库中保存记录 targetEm.persist(record); count++; // 每100条记录提交一次事务 if (count % 100 == 0) { targetEm.flush(); targetEm.clear(); } } // 提交剩余记录 if (count % 100 != 0) { targetEm.flush(); } logger.info("成功迁移 " + count + " 条记录"); return count; } /** * 完整表迁移 */ public int fullMigration() { // 获取所有记录 List<Alp3DHoleDetailLayer> records = sourceEm.createQuery( "SELECT o FROM Alp3DHoleDetailLayer o", Alp3DHoleDetailLayer.class) .getResultList(); int count = 0; for (Alp3DHoleDetailLayer record : records) { // 在目标数据库中保存记录 targetEm.persist(record); count++; // 每100条记录提交一次事务 if (count % 100 == 0) { targetEm.flush(); targetEm.clear(); } } // 提交剩余记录 if (count % 100 != 0) { targetEm.flush(); } logger.info("成功迁移 " + count + " 条记录"); return count; } } ``` ### 2. 持久化单元配置 (persistence.xml) ```xml <!-- 源数据库配置 (SQL Server) --> <persistence-unit name="source-db" transaction-type="JTA"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <jta-data-source>java:/SourceDS</jta-data-source> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"/> <property name="hibernate.hbm2ddl.auto" value="validate"/> </properties> </persistence-unit> <!-- 目标数据库配置 (MySQL) --> <persistence-unit name="target-db" transaction-type="JTA"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <jta-data-source>java:/TargetDS</jta-data-source> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.jdbc.batch_size" value="100"/> </properties> </persistence-unit> ``` ### 3. 迁移控制器 ```java @Named @ViewScoped public class MigrationController implements Serializable { private Date lastMigrationDate; private int migratedCount; private MigrationStatus status = MigrationStatus.READY; @Inject private MigrationService migrationService; public void prepareMigration() { // 获取最早创建日期作为迁移起点 lastMigrationDate = migrationService.getEarliestCreateDate(); status = MigrationStatus.PREPARED; } public void executeMigration() { if (lastMigrationDate == null) { prepareMigration(); } status = MigrationStatus.RUNNING; try { migratedCount = migrationService.migrateData(lastMigrationDate); status = MigrationStatus.COMPLETED; } catch (Exception e) { status = MigrationStatus.FAILED; // 处理异常 } } public void executeFullMigration() { status = MigrationStatus.RUNNING; try { migratedCount = migrationService.fullMigration(); status = MigrationStatus.COMPLETED; } catch (Exception e) { status = MigrationStatus.FAILED; // 处理异常 } } // 枚举定义 public enum MigrationStatus { READY, PREPARED, RUNNING, COMPLETED, FAILED } // Getter 和 Setter } ``` ## 迁移工作流程解释 1. **准备阶段**: - 使用`findAlp3DHoleDetailLayer`查询获取最早创建日期 - 此日期作为增量迁移的起始点 2. **增量迁移**: - 查询`createDate > lastMigrationDate`的所有记录 - 分批迁移到MySQL数据库(每100条提交一次) 3. **全量迁移**: - 迁移整个表的数据 - 适用于初始迁移或数据量不大的情况 4. **状态管理**: - 跟踪迁移过程状态(准备中、运行中、完成、失败) - 提供迁移记录计数 ## MySQL迁移注意事项 1. **数据类型映射**: - SQL Server的`datetime` → MySQL的`datetime(3)` - SQL Server的`float` → MySQL的`double` - SQL Server的`bit` → MySQL的`tinyint(1)` 2. **命名差异处理**: ```java // MySQL实体类调整 @Entity @Table(name = "alp_3d_hole_detail_layer") // MySQL表名通常小写 public class Alp3DHoleDetailLayer { // 列名调整 @Column(name = "hole_layer_id") private int holeLayerId; // 其他字段类似调整... } ``` 3. **批量优化**: - 使用`hibernate.jdbc.batch_size`优化批量插入 - 定期清除持久化上下文防止内存溢出 4. **迁移验证**: ```java // 验证记录数 Long sourceCount = sourceEm.createQuery("SELECT COUNT(o) FROM Alp3DHoleDetailLayer o", Long.class) .getSingleResult(); Long targetCount = targetEm.createQuery("SELECT COUNT(o) FROM Alp3DHoleDetailLayer o", Long.class) .getSingleResult(); if (!sourceCount.equals(targetCount)) { throw new MigrationException("记录数不匹配: 源=" + sourceCount + ", 目标=" + targetCount); } ``` ## 迁移策略建议 1. **初始迁移**: - 执行全量迁移(`fullMigration`) - 记录最后一条记录的`createDate` 2. **增量迁移**: - 定时任务执行增量迁移 - 每次迁移后更新最后迁移日期 3. **错误处理**: - 记录迁移失败的记录ID - 提供重试机制 - 实现错误记录跳过选项 4. **性能优化**: - 对大表使用分页迁移 - 并行处理多个表 - 禁用目标数据库索引(迁移完成后重建)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值