spring 管理 hibernate 事务时,entity 类的主键必须设为 Long 或 Integer 吗?

2014-11-07 18:34:02 +08:00
 forreal
我之前写了一个entity类,@Id注解在String类型的name字段上。(因为就这一个字段,另外加一个id字段浪费)。
然后当我session.save()时,不生成insert语句,也不插入数据库。除非我session.flush()一下才插入数据库。
而当我把entity类增加一个Long类型的id字段并且设置自动增长时,再session.save()时,就既生成insert语句又插入字段。
请问这是为什么呢?
7670 次点击
所在节点    Java
13 条回复
welsmann
2014-11-07 21:33:03 +08:00
根据需要加入@GeneratedValue注解
qping
2014-11-07 22:29:59 +08:00
下面是百度搜的,印象中是这样的。
"session在什么情况下执行flush:默认在事务提交时;显示的调用flush;在执行查询前,如:iterate.
调用save后,只是将对象纳入到了session的管理,不会发出insert语句,如没有主键会根据主键生成策略生成主键,把脱管或瞬态对象变为持久态对象."

http://zhidao.baidu.com/link?url=p8o00ykbezRThHvydBXl3RvUZFO1cvOTB-XcD3m5I2K1idgOcSdTAP2GAC-xEaJOCe01G5S7mOSfMpzzvH5m-K
shuson
2014-11-08 10:10:40 +08:00
@GeneratedValue(strategy = GenerationType.AUTO) cannot be used with String type. A straightforward solution could be to use the @PrePersist annotation on your entity class.
forreal
2014-11-08 17:26:08 +08:00
@welsmann 主键我想自己指定,不想自动生成。但是我试了如果实体的主键是Long类型却不加@GeneratedValue 也会save()时不插入数据库。

@qping 如果不加@GeneratedValue到主键上,commit()时就不会插入数据库。不知道为什么。(哪怕我手动指定了主键)

@shuson 意思是先用自动生成主键,然后再@PrePersist注解的方法 改对象的主键吗?
qping
2014-11-08 19:23:44 +08:00
有其他语句吗 比如update语句,没有错误日志,相关配置,只能等高手过来帮你猜了 - -
qping
2014-11-08 19:29:41 +08:00
我始终不认为你错的地方在于注解GeneratedValue 或者 主键Long类型。
事务提交后没有insert语句,应该是事务配置或者hibernate对象状态。

http://www.baidu.com/s?wd=hibernate%E5%AF%B9%E8%B1%A1%E7%8A%B6%E6%80%81
http://www.baidu.com/s?wd=hibernate%20%E4%B8%BB%E9%94%AE%E7%94%9F%E6%88%90

我曾经因为spring mvc传递的是 持久态的对象,修改其主键后怎么改后台都是update,而我想要的是insert ,楼主看是不是这种情况
forreal
2014-11-08 19:46:42 +08:00
@qping 没有错误日志(控制台上sql语句,错误日志什么都没有),
配置都是照https://github.com/ZhibingXie/SpringMVC-Spring-Hibernate来配的。
就是有一个实体类,id用的是名字(String类型),而且没有注解@GeneratedValue,然后事务提交不发出insert语句也不存入数据库。必须手动调用flush()才行。
但是如果在那个实体类上额外加上一个int或long的id,并且注解@GeneratedValue(我感觉是问题的关键),配置什么的都不用改就可以不手动调用flush(),而是事务提交时就自动发出insert语句并插入数据库了。
forreal
2014-11-08 19:47:37 +08:00
@forreal
@qping
没有错误日志(控制台上什么都没有)
qping
2014-11-08 20:03:51 +08:00
你的代码呢
forreal
2014-11-09 23:13:26 +08:00
@qping

model层:
@Entity
public class Node {

private String name;

@Id
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

dao层:
baseDaoImpl.java:

public class BaseDaoImpl {
private SessionFactory sessionFactory;

public SessionFactory getSessionFactory() {
return sessionFactory;
}

@Resource(name = "sessionFactory")
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Session getCurrentSession(){
return sessionFactory.getCurrentSession();
}
}

NodeDaoImpl.java:

@Repository
public class NodeDaoImpl extends BaseDaoImpl implements NodeDao {

@Override
public Serializable save(Node node){
Session session = getCurrentSession();
session.setFlushMode(FlushMode.COMMIT);
Serializable id = session.save(node);
//session.flush();

return id;
}
}

service层:

@Service
public class NodeServiceImpl implements NodeService {
@Override
public Node add(Node node) {
int id = (Integer)nodeDao.save(node);
return node;

}
@Resource
public void setNodeDao(NodeDao nodeDao) {
this.nodeDao = nodeDao;
}
private NodeDao nodeDao;
}

controller层(spring mvc):

@Controller
@RequestMapping("/admin")
public class AdminController {
@RequestMapping
public String index(){
return "admin/admin";
}

@RequestMapping(value = "/node")
public String addNode(@RequestParam("name")String nodename,ModelMap model){
Node node = new Node();
node.setName(nodename);
nodeService.add(node);
model.addAttribute("msg","添加成功");
return "admin/admin";

}

@Resource
public void setNodeService(NodeService nodeService) {
this.nodeService = nodeService;
}

private NodeService nodeService;
}

spring-hibernate.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 数据库连接配置 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${driveClassName}" />
<property name="url" value="${jdbc_url}" />
<property name="username" value="${jdbc_name}" />
<property name="password" value="${jdbc_password}" />

<property name="initialSize" value="5" />
<property name="maxConnLifetimeMillis" value="60000" />
<property name="maxTotal" value="20" />
<property name="maxIdle" value="5" />
</bean>

<!-- Hibernate属性配置 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
<property name="packagesToScan">
<list>
<value>model</value>
</list>
</property>
</bean>


<!-- 事务配置 -->
<!-- 需要引入tx的命名空间 -->
<!-- 这是事务通知操作,使用的事务管理器引用自 transactionManager -->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 指定哪些方法需要加入事务,这里懒惰一下全部加入,可以使用通配符来只加入需要的方法 -->
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="edit*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="get*" propagation="REQUIRED" read-only="true"/>
<tx:method name="is*" propagation="REQUIRED" read-only="true"/>
<tx:method name="follow*" propagation="REQUIRED" />
<!--<tx:method name="*" propagation="REQUIRED" />-->
</tx:attributes>
</tx:advice>
<!-- 需要引入aop的命名空间 -->
<aop:config proxy-target-class="true">
<!-- 切入点指明了在执行Service的所有方法时产生事务拦截操作 -->
<aop:pointcut id="transactionPointcut" expression="execution(* service.Impl.*.*(..))" />
<!-- 定义了将采用何种拦截操作,这里引用到 txAdvice -->
<aop:advisor pointcut-ref="transactionPointcut" advice-ref="txAdvice" />
</aop:config>
</beans>
qping
2014-11-10 09:03:05 +08:00
没看出哪有问题

<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>

改成这样试试
qping
2014-11-10 09:06:38 +08:00
或者在service类上加 @Transactional 标签

@Service
@Transactional
public class NodeServiceImpl implements NodeService {
@Override
public Node add(Node node) {
int id = (Integer)nodeDao.save(node);
return node;

}
forreal
2014-11-10 13:20:26 +08:00
@qping

xml加rollback-for="Exception" 和利用@Transactional都分别试过了,还是不行。
还是需要显式调用flush()或者给实体加一个额外的id数字字段并且@GeneratedValue才可以插入数据库

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/144757

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX