是否应将FK设为另一表索引?Hibernate类型字符串关联咨询
解决Hibernate中外键字段通过类型名称关联的问题
你遇到的这个场景,Hibernate完全有成熟的方案来处理——通过建立实体间的关联映射,而不是手动去查询索引值。目前你的代码里把type作为普通字符串属性处理,但实际上它是外键,对应着另一个类型表(比如叫TestType)的主键,正确的做法是让TestCase和这个类型实体建立关联,让Hibernate帮你自动管理外键的存储。
步骤1:创建类型实体(对应你的外键关联表)
首先你需要一个对应类型表的实体,假设你的类型表结构是id(主键)、typeName(类型名称):
public class TestType { private int id; private String typeName; public TestType() {} public TestType(String typeName) { this.typeName = typeName; } // getter和setter public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTypeName() { return typeName; } public void setTypeName(String typeName) { this.typeName = typeName; } }
对应的Hibernate XML映射(TestType.hbm.xml):
<hibernate-mapping> <class name="com.atp.Model.TestCases.TestType" table="test_types"> <id name="id" type="int" column="id"> <generator class="native"/> </id> <property name="typeName" column="type_name" type="string" unique="true"/> </class> </hibernate-mapping>
步骤2:修改TestCase实体,建立多对一关联
把原来的String type替换成TestType类型的属性,因为一个TestCase对应一个类型,是多对一的关系:
public class TestCase { private int id; private String name; // 替换原来的String type为TestType对象 private TestType type; private byte[] data; private String creationDate; private String createdBy; public TestCase() { } // 修改构造器,接收TestType而非String public TestCase(String name, TestType type, byte[] data, String creationDate, String createdBy) { this.name = name; this.type = type; this.data = data; this.creationDate = creationDate; this.createdBy = createdBy; } // getter和setter public TestType getType() { return type; } public void setType(TestType type) { this.type = type; } // 其他getter、setter保持不变 ... }
然后修改TestCase的Hibernate映射文件,把原来的property标签换成many-to-one,指定关联的实体和外键字段:
<hibernate-mapping> <class name="com.atp.Model.TestCases.TestCase" table="testCases"> <meta attribute="class-description"> This class contains the testCases details. </meta> <id name="id" type="int" column="idtestCase"> <generator class="native"/> </id> <property name="name" column="name" type="string"/> <!-- 替换原来的type property为many-to-one关联 --> <many-to-one name="type" class="com.atp.Model.TestCases.TestType" column="type" fetch="select"> <column name="type" not-null="true"/> </many-to-one> <property name="data" column="data" type="binary"/> <property name="creationDate" column="creationDate" type="string"/> <property name="createdBy" column="createdBy" type="string"/> </class> </hibernate-mapping>
步骤3:修改addTestCase方法,通过类型名称获取关联实体
现在你可以在添加TestCase时,先根据类型名称从数据库中查询对应的TestType实体,再传入TestCase的构造器——Hibernate会自动把TestType的id存入testCases表的type外键字段:
public static void addTestCase(String testName, String typeName, MultipartFile file, String user) { byte[] fileByte; Date date = new Date(); final DateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); String creationDate = sdf.format(date); try { fileByte = file.getBytes(); // 1. 根据类型名称查询TestType实体(这里假设你有获取Session的方法) Session session = Database.getSession(); TestType testType = (TestType) session.createQuery("FROM TestType WHERE typeName = :typeName") .setParameter("typeName", typeName) .uniqueResult(); // 2. 如果类型不存在,可以选择创建新类型(根据你的业务需求调整) if (testType == null) { testType = new TestType(typeName); session.save(testType); } // 3. 创建TestCase并保存 TestCase testCase = new TestCase(testName, testType, fileByte, creationDate, user); Database.addToDatabase(testCase); session.close(); } catch (IOException e) { e.printStackTrace(); } }
为什么这样做?
- 这完全符合ORM的设计思想:用实体对象来表示数据库间的关联,而不是手动处理外键的数值。
- Hibernate会自动帮你管理外键的插入和查询,不需要你手动去获取索引值。
- 后续查询TestCase时,你可以直接通过
testCase.getType().getTypeName()获取类型名称,不需要额外的关联查询(如果配置了合适的fetch策略)。
如果你实在不想修改实体结构(不推荐),也可以通过自定义Hibernate类型或者使用@Formula注解来映射,但这种方式不够直观,也不符合最佳实践,所以还是建议使用实体关联的方案。
内容的提问来源于stack exchange,提问作者user9176898
相关产品推荐
相关产品推荐

