查询休眠脏检查



我正在阅读 Java Persistence with Hibernate 2nd Edition。我从书中找到了下面这段我不清楚的引文。

另一个需要考虑的问题是脏检查。自动休眠 检测状态更改,以便将更新的状态与 数据库。通常,从 返回不同的实例是安全的 getter 方法比 Hibernate 传递给 setter 的实例。 Hibernate通过值(而不是对象标识)比较它们,以确定 是否需要更新属性的持久状态。为 示例,以下 getter 方法不会导致不必要的 SQL 更新:

public String getFirstName(){
return new String(firstName);
}

我的问题是为什么返回一个新的字符串对象不会进行不必要的 SQL 更新,它与只返回名字有什么不同?

当我尝试运行 getFirstName 时,我没有看到任何更新查询被触发。

请告诉我,因为我不清楚。

下面是代码:

package org.prashdeep.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class User {

protected Long id;
@Id
@GeneratedValue()
public Long getId() { // Optional but useful
return id;
}
protected String firstName;
protected String lastName;
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
System.out.println("Called from the setter of lastname");
this.lastName = lastName;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", firstName='" + firstName + ''' +
", lastName='" + lastName + ''' +
'}';
}
}

保存用户实体.java

package org.prashdeep.executor;
import bitronix.tm.resource.jdbc.PoolingDataSource;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataBuilder;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Environment;
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
import org.hibernate.service.ServiceRegistry;
import org.prashdeep.common.DatabaseProduct;
import org.prashdeep.model.User;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.transaction.Status;
import javax.transaction.UserTransaction;
import java.util.List;

public class SaveUserEntity {
protected Context context;
protected PoolingDataSource dataSource;
public static String DATASOURCE_NAME = "myDS";
public DatabaseProduct databaseProduct;

public UserTransaction getUserTransaction() {
try {
return (UserTransaction) context.lookup("java:comp/UserTransaction");
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public void rollback() {
UserTransaction tx = getUserTransaction();
try {
if (tx.getStatus() == Status.STATUS_ACTIVE ||
tx.getStatus() == Status.STATUS_MARKED_ROLLBACK)
tx.rollback();
} catch (Exception ex) {
System.err.println("Rollback of transaction failed, trace follows!");
ex.printStackTrace(System.err);
}
}
protected SessionFactory createSessionFactory()  throws Exception{
this.dataSource = new PoolingDataSource();
dataSource.setUniqueName(DATASOURCE_NAME);
dataSource.setMinPoolSize(1);
dataSource.setMaxPoolSize(5);
dataSource.setPreparedStatementCacheSize(10);
dataSource.setIsolationLevel("READ_COMMITTED");
dataSource.setClassName("org.h2.jdbcx.JdbcDataSource");
dataSource.setAllowLocalTransactions(true);
dataSource.getDriverProperties().put(
"URL",
"jdbc:h2:mem:test"
);

dataSource.getDriverProperties().put("user", "sa");
context = new InitialContext();
dataSource.init();

/*
This builder helps you create the immutable service registry with
chained method calls.
*/
StandardServiceRegistryBuilder serviceRegistryBuilder =
new StandardServiceRegistryBuilder();
/*
Configure the services registry by applying settings.
*/
serviceRegistryBuilder
.applySetting("hibernate.connection.datasource", "myDS")
.applySetting("hibernate.format_sql", "true")
.applySetting("hibernate.show_sql", "true")
.applySetting("hibernate.hbm2ddl.auto", "create-drop");
// Enable JTA (this is a bit crude because Hibernate devs still believe that JTA is
// used only in monstrous application servers and you'll never see this code).
serviceRegistryBuilder.applySetting(
Environment.TRANSACTION_COORDINATOR_STRATEGY,
JtaTransactionCoordinatorBuilderImpl.class
);
ServiceRegistry serviceRegistry = serviceRegistryBuilder.build();
/*
You can only enter this configuration stage with an existing service registry.
*/
MetadataSources metadataSources = new MetadataSources(serviceRegistry);
/*
Add your persistent classes to the (mapping) metadata sources.
*/
metadataSources.addAnnotatedClass(
org.prashdeep.model.User.class
);
// Add hbm.xml mapping files
// metadataSources.addFile(...);
// Read all hbm.xml mapping files from a JAR
// metadataSources.addJar(...)
MetadataBuilder metadataBuilder = metadataSources.getMetadataBuilder();
Metadata metadata = metadataBuilder.build();

SessionFactory sessionFactory = metadata.buildSessionFactory();
return sessionFactory;
}


public static void main(String args[]) throws Exception {
SaveUserEntity obj = new SaveUserEntity();
SessionFactory sessionFactory = obj.createSessionFactory();
try {
{
/*
Get access to the standard transaction API <code>UserTransaction</code> and
begin a transaction on this thread of execution.
*/
UserTransaction tx = obj.getUserTransaction();
tx.begin();
/*
Whenever you call <code>getCurrentSession()</code> in the same thread you get
the same <code>org.hibernate.Session</code>. It's bound automatically to the
ongoing transaction and is closed for you automatically when that transaction
commits or rolls back.
*/
Session session = sessionFactory.getCurrentSession();
User user = new User();
user.setFirstName("Pradeep");
user.setLastName("Kumar");
/*
The native Hibernate API is very similar to the standard Java Persistence API and most methods
have the same name.
*/
session.persist(user);

/*
Hibernate synchronizes the session with the database and closes the "current"
session on commit of the bound transaction automatically.
*/
tx.commit();
}
{
UserTransaction tx = obj.getUserTransaction();
tx.begin();

/*
A Hibernate criteria query is a type-safe programmatic way to express queries,
automatically translated into SQL.
*/
List<User> users =
sessionFactory.getCurrentSession().createCriteria(
User.class
).list();
// SELECT * from MESSAGE
users.get(0).setFirstName("Praveen");
System.out.println(users.get(0).getFirstName());

tx.commit();
}
} finally {
obj.rollback();
}
}
}

假设在数据库中,您有一条记录,其中firstNameFOO。 在某些时候,此记录作为持久实体加载,因此您现在有一个类型为User的 Java 对象,其中getFirstName()返回一个字符串"FOO"

当您使用setFirstName("BAR");修改持久对象时,下次提交此内容时,将导致 SQL 更新。这是因为Hibernate会将"数据库状态"与"内存状态"进行比较,它会看到存在差异(FOO已经变得BAR),他将启动SQL UPDATE。到目前为止都是标准的。

现在,《冬眠手册》试图表达的观点是,这种比较是基于equals而不是==。在我们的示例中,它将检查字符串值FOOBAR是否相等(概念上oldValue.equals(newValue)),而不是是否返回相同的字符串对象(oldValue == newValue)。

因此,他们的例子:您可以返回new String(firstName)这是一个不同的 String 对象,但equals相同。

希望对您有所帮助。

最新更新