在 Spring / JPA 中读取未提交的生成 ID
我试图检索事务中新创建的实体的生成 ID,但当我尝试读取 ID 值时,它为空。我认为这是因为事务尚未提交并且实体的 ID 尚未创建。
我正在使用 Spring MVC 和事务(在我的服务上使用 @Transactional),并使用 JPA 作为数据层。我不是事务管理方面的专家,所以我什至不确定这是否可能。这是在表示层(Spring portlet MVC)中执行的示例代码:
Long parentId = getParentId();
Folder parentFolder = linksService.getItem(parentId, Folder.class);
Folder newFolder;
newFolder = new Folder();
newFolder.setName("new folder");
newFolder.setParent(parentFolder);
parentFolder.addItem(newItem);
linksService.saveItem(parentFolder); // this calls entityManager.merge(parentFolder)
// this returns null
String itemId = newFolder.getItemId();
编辑:
这是实体。我正在使用 Oracle 数据库。
@Entity
@Table(name = "LINK_ITEM")
@DiscriminatorColumn(name = "ITEM_TYPE")
public abstract class Item {
/**
* The Id of this item
*/
@Id
@TableGenerator(name = "table_gen", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.TABLE,
generator = "table_gen")
@Column(name = "ITEM_ID")
private Long itemId;
/**
* The name of this item
*/
private String name;
/**
* the parent item of this item
*/
@ManyToOne
@JoinColumn(name="PARENT_ID")
private Item parent;
/**
* The user ID that owns this item
*/
private String owner;
/**
* @return Returns the itemId.
*/
public Long getItemId() {
return itemId;
}
/**
* @param itemId
* The itemId to set.
*/
public void setItemId(Long itemId) {
this.itemId = itemId;
}
/**
* @return Returns the name.
*/
public String getName() {
return name;
}
/**
* @param name
* The name to set.
*/
public void setName(String name) {
this.name = name;
}
/**
* @return Returns the owner.
*/
public String getOwner() {
return owner;
}
/**
* @param owner
* The owner to set.
*/
public void setOwner(String owner) {
this.owner = owner;
}
/**
* @return Returns the parent.
*/
public Item getParent() {
return parent;
}
/**
* @param parent
* The parent to set.
*/
public void setParent(Item parent) {
this.parent = parent;
}
/**
* Returns the depth of this object in the folder tree. 0 is the top folder,
* 1 is one level down, etc.
*
* @return Returns the depth.
*/
@Transient
public long getDepth() {
long i = 0;
for (Item item = this; item.getParent() != null; item = item
.getParent()) {
++i;
}
return i;
}
/**
* Changes the parent folder of this item and updates links / children
* appropriately.
*
* @param parentFolder
*/
public void updateParent(Folder parentFolder) {
removeFromParent();
parentFolder.addItem(this);
}
/**
* Removes this item from it's parent folder, if it has one.
*/
public void removeFromParent() {
if (getParent() != null) {
((Folder) getParent()).removeItem(this);
}
}
public void moveUp() {
if (getParent() == null) {
return;
}
Folder parent = (Folder) getParent();
List<Item> siblings = parent.getChildren();
int index = siblings.indexOf(this);
if (index > 0) {
Item previousItem = siblings.get(index - 1);
siblings.set(index, previousItem);
siblings.set(index - 1, this);
}
}
public void moveDown() {
if (getParent() == null) {
return;
}
Folder parent = (Folder) getParent();
List<Item> siblings = parent.getChildren();
int index = siblings.indexOf(this);
int numItems = siblings.size();
if ((numItems > 1) && (index < (numItems - 1))) {
Item nextItem = (Item) siblings.get(index + 1);
siblings.set(index, nextItem);
siblings.set(index + 1, this);
}
}
/**
* Returns the String representation of this Item.
*/
@Override
public String toString() {
return "itemId=" + this.getItemId() + "; name=" + this.getName()
+ "; owner=" + this.getOwner();
}
}
@Entity
@DiscriminatorValue("F")
public class Folder extends Item {
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name="PARENT_ID", referencedColumnName="ITEM_ID")
private List<Item> children = new ArrayList<Item>();
@Transient
private String path;
@Transient
private boolean open;
@Transient
private Collection<Link> orderedLinks;
/**
* @return Returns the children.
*/
public List<Item> getChildren() {
return children;
}
/**
* @param children
* The children to set.
*/
public void setChildren(List<Item> children) {
this.children = children;
}
/**
* Changes the parent folder of this item and updates links / children
* appropriately.
*
* @param parentFolder
*/
public void updateParent(Folder parentFolder) {
super.updateParent(parentFolder);
// update the path since the parent folder has changed
updatePath();
}
/**
* @param newItem
*/
public void addItem(Item newItem) {
newItem.setParent(this);
getChildren().add(newItem);
}
/**
* @param item
*/
public void removeItem(Item item) {
getChildren().remove(item);
item.setParent(null);
}
/**
*
* @param items
*/
public void addItems(List<? extends Item> items) {
for (Item item : items)
addItem(item);
}
/**
*
* @param items
*/
public void removeItems(List<? extends Item> items) {
for (Item item : items)
removeItem(item);
}
/**
* Returns a list of Folder objects that are the subfolders of this folder.
* This folder is also included at the top of the list.
*
* @return
* @throws ServiceException
*/
@Transient
public List<Folder> getFolderList() {
List<Folder> folderList = new ArrayList<Folder>();
buildFolderList(folderList, null);
return folderList;
}
/**
* Returns a list of Folder objects that are the subfolders of this folder.
* This folder is also included at the top of the list. This method will
* exclude the "excludeFolder" and it's subfolders from the list.
*
* @param excludeFolder
* @return
*/
@Transient
public List<Folder> getFolderList(Folder excludeFolder) {
List<Folder> folderList = new ArrayList<Folder>();
buildFolderList(folderList, excludeFolder);
return folderList;
}
/**
* Returns a recursive list of the parent folder of this folder. Includes
* this folder in the list.
*
* @return
*/
@Transient
public List<Folder> getParentList() {
List<Folder> parentList = new ArrayList<Folder>();
Folder currentFolder = this;
parentList.add(currentFolder);
while (currentFolder.getParent() != null) {
currentFolder = (Folder) currentFolder.getParent();
parentList.add(currentFolder);
}
// reverse the ordering
Collections.reverse(parentList);
return parentList;
}
/**
* Private method called recursively to build a list of Folder's and
* subfolders of the parentFolder.
*
* @param folderList
* @param parentFolder
* @param excludeFolder
*/
private void buildFolderList(List<Folder> folderList, Folder excludeFolder) {
// Don't add the exclude folder to the list
if (excludeFolder != null && this.equals(excludeFolder)) {
return;
}
folderList.add(this);
if (!isFolderEmpty()) {
for (Item item : getChildren()) {
if (item instanceof Folder) {
((Folder) item).buildFolderList(folderList, excludeFolder);
}
}
}
}
/**
* @return Returns the folderEmpty.
*/
@Transient
public boolean isFolderEmpty() {
return children == null || children.isEmpty() || children.size() == 0;
}
/**
*
*/
private void updatePath() {
StringBuffer strBuffer = new StringBuffer("");
strBuffer.append(getName());
Item parent = getParent();
while (parent != null) {
strBuffer.insert(0, parent.getName() + " > ");
parent = parent.getParent();
}
this.path = strBuffer.toString();
}
/**
* @return Returns the path of this folder.
*/
public String getPath() {
if (this.path == null || this.path.length() == 0)
updatePath();
return this.path;
}
/**
* @param path
* The path to set.
*/
protected void setPath(String path) {
this.path = path;
}
public Item find(Long itemId) {
if (itemId.equals(getItemId()))
return this;
Item item = null;
List<Item> children = getChildren();
for (Item currentItem : children) {
if (currentItem.getItemId().equals(itemId)) {
item = currentItem;
break;
} else if (currentItem instanceof Folder) {
item = ((Folder) currentItem).find(itemId);
if (item != null)
break;
}
}
return item;
}
/**
* Returns the String representation of this Folder.
*/
@Override
public String toString() {
return super.toString() + "; path=" + this.getPath();
}
/**
*
* @return a list of Link objects that this Folder holds.
*/
@Transient
public List<Link> getLinks() {
List<Item> children = getChildren();
List<Link> links = new ArrayList<Link>(children.size()
- (children.size() / 2));
for (Item item : children) {
if (item instanceof Link) {
links.add((Link) item);
}
}
return links;
}
/**
* Returns the child Folders of this Folder and their child Folders, etc.
*
* @return
*/
@Transient
public List<Folder> getChildFolders() {
List<Folder> folderList = new ArrayList<Folder>();
buildFolderList(folderList, null);
folderList.remove(this);
return folderList;
}
public boolean isOpen() {
return open;
}
@Transient
public boolean isClosed() {
return !open;
}
public void setOpen(boolean open) {
this.open = open;
}
public Collection<Link> getOrderedLinks() {
return orderedLinks;
}
public void setOrderedLinks(Collection<Link> orderedLinks) {
this.orderedLinks = orderedLinks;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || (obj.getClass() != this.getClass())) {
return false;
}
Folder folder = (Folder) obj;
return this.getItemId() == folder.getItemId();
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int var = (int) (this.getItemId().longValue() ^ (this.getItemId()
.longValue() >>> 32));
int hash = 7;
hash = 31 * hash + var;
return hash;
}
}
@Entity
@DiscriminatorValue("L")
public class Link extends Item {
private String url;
public Link() {
}
public Link(String url) {
this.url = url;
}
/**
* @return Returns the url.
*/
public String getUrl() {
return url;
}
/**
* @param url
* The url to set.
*/
public void setUrl(String url) {
if (url != null && url.indexOf(":/") == -1)
url = "http://" + url;
this.url = url;
}
}
控制器正在调用DAO,DAO调用entityManager.merge()(我尝试包括entityManger.flush())。我还使用 OpenEntityInManagerInterceptor。
I am trying to retrieve the generated ID of a newly created Entity within a transaction, but when I try to read the ID value it is null. I assume this is because the transaction has not yet been committed and the Entity's ID has yet to be created.
I am using Spring MVC and transactions (using @Transactional on my service), and using JPA for the data layer. I'm not an expert in transaction management, so I'm not even sure if this is possible. This is example code being executing in the presentation layer (Spring portlet MVC):
Long parentId = getParentId();
Folder parentFolder = linksService.getItem(parentId, Folder.class);
Folder newFolder;
newFolder = new Folder();
newFolder.setName("new folder");
newFolder.setParent(parentFolder);
parentFolder.addItem(newItem);
linksService.saveItem(parentFolder); // this calls entityManager.merge(parentFolder)
// this returns null
String itemId = newFolder.getItemId();
EDIT:
Here are the entities. I am using Oracle db.
@Entity
@Table(name = "LINK_ITEM")
@DiscriminatorColumn(name = "ITEM_TYPE")
public abstract class Item {
/**
* The Id of this item
*/
@Id
@TableGenerator(name = "table_gen", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.TABLE,
generator = "table_gen")
@Column(name = "ITEM_ID")
private Long itemId;
/**
* The name of this item
*/
private String name;
/**
* the parent item of this item
*/
@ManyToOne
@JoinColumn(name="PARENT_ID")
private Item parent;
/**
* The user ID that owns this item
*/
private String owner;
/**
* @return Returns the itemId.
*/
public Long getItemId() {
return itemId;
}
/**
* @param itemId
* The itemId to set.
*/
public void setItemId(Long itemId) {
this.itemId = itemId;
}
/**
* @return Returns the name.
*/
public String getName() {
return name;
}
/**
* @param name
* The name to set.
*/
public void setName(String name) {
this.name = name;
}
/**
* @return Returns the owner.
*/
public String getOwner() {
return owner;
}
/**
* @param owner
* The owner to set.
*/
public void setOwner(String owner) {
this.owner = owner;
}
/**
* @return Returns the parent.
*/
public Item getParent() {
return parent;
}
/**
* @param parent
* The parent to set.
*/
public void setParent(Item parent) {
this.parent = parent;
}
/**
* Returns the depth of this object in the folder tree. 0 is the top folder,
* 1 is one level down, etc.
*
* @return Returns the depth.
*/
@Transient
public long getDepth() {
long i = 0;
for (Item item = this; item.getParent() != null; item = item
.getParent()) {
++i;
}
return i;
}
/**
* Changes the parent folder of this item and updates links / children
* appropriately.
*
* @param parentFolder
*/
public void updateParent(Folder parentFolder) {
removeFromParent();
parentFolder.addItem(this);
}
/**
* Removes this item from it's parent folder, if it has one.
*/
public void removeFromParent() {
if (getParent() != null) {
((Folder) getParent()).removeItem(this);
}
}
public void moveUp() {
if (getParent() == null) {
return;
}
Folder parent = (Folder) getParent();
List<Item> siblings = parent.getChildren();
int index = siblings.indexOf(this);
if (index > 0) {
Item previousItem = siblings.get(index - 1);
siblings.set(index, previousItem);
siblings.set(index - 1, this);
}
}
public void moveDown() {
if (getParent() == null) {
return;
}
Folder parent = (Folder) getParent();
List<Item> siblings = parent.getChildren();
int index = siblings.indexOf(this);
int numItems = siblings.size();
if ((numItems > 1) && (index < (numItems - 1))) {
Item nextItem = (Item) siblings.get(index + 1);
siblings.set(index, nextItem);
siblings.set(index + 1, this);
}
}
/**
* Returns the String representation of this Item.
*/
@Override
public String toString() {
return "itemId=" + this.getItemId() + "; name=" + this.getName()
+ "; owner=" + this.getOwner();
}
}
@Entity
@DiscriminatorValue("F")
public class Folder extends Item {
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name="PARENT_ID", referencedColumnName="ITEM_ID")
private List<Item> children = new ArrayList<Item>();
@Transient
private String path;
@Transient
private boolean open;
@Transient
private Collection<Link> orderedLinks;
/**
* @return Returns the children.
*/
public List<Item> getChildren() {
return children;
}
/**
* @param children
* The children to set.
*/
public void setChildren(List<Item> children) {
this.children = children;
}
/**
* Changes the parent folder of this item and updates links / children
* appropriately.
*
* @param parentFolder
*/
public void updateParent(Folder parentFolder) {
super.updateParent(parentFolder);
// update the path since the parent folder has changed
updatePath();
}
/**
* @param newItem
*/
public void addItem(Item newItem) {
newItem.setParent(this);
getChildren().add(newItem);
}
/**
* @param item
*/
public void removeItem(Item item) {
getChildren().remove(item);
item.setParent(null);
}
/**
*
* @param items
*/
public void addItems(List<? extends Item> items) {
for (Item item : items)
addItem(item);
}
/**
*
* @param items
*/
public void removeItems(List<? extends Item> items) {
for (Item item : items)
removeItem(item);
}
/**
* Returns a list of Folder objects that are the subfolders of this folder.
* This folder is also included at the top of the list.
*
* @return
* @throws ServiceException
*/
@Transient
public List<Folder> getFolderList() {
List<Folder> folderList = new ArrayList<Folder>();
buildFolderList(folderList, null);
return folderList;
}
/**
* Returns a list of Folder objects that are the subfolders of this folder.
* This folder is also included at the top of the list. This method will
* exclude the "excludeFolder" and it's subfolders from the list.
*
* @param excludeFolder
* @return
*/
@Transient
public List<Folder> getFolderList(Folder excludeFolder) {
List<Folder> folderList = new ArrayList<Folder>();
buildFolderList(folderList, excludeFolder);
return folderList;
}
/**
* Returns a recursive list of the parent folder of this folder. Includes
* this folder in the list.
*
* @return
*/
@Transient
public List<Folder> getParentList() {
List<Folder> parentList = new ArrayList<Folder>();
Folder currentFolder = this;
parentList.add(currentFolder);
while (currentFolder.getParent() != null) {
currentFolder = (Folder) currentFolder.getParent();
parentList.add(currentFolder);
}
// reverse the ordering
Collections.reverse(parentList);
return parentList;
}
/**
* Private method called recursively to build a list of Folder's and
* subfolders of the parentFolder.
*
* @param folderList
* @param parentFolder
* @param excludeFolder
*/
private void buildFolderList(List<Folder> folderList, Folder excludeFolder) {
// Don't add the exclude folder to the list
if (excludeFolder != null && this.equals(excludeFolder)) {
return;
}
folderList.add(this);
if (!isFolderEmpty()) {
for (Item item : getChildren()) {
if (item instanceof Folder) {
((Folder) item).buildFolderList(folderList, excludeFolder);
}
}
}
}
/**
* @return Returns the folderEmpty.
*/
@Transient
public boolean isFolderEmpty() {
return children == null || children.isEmpty() || children.size() == 0;
}
/**
*
*/
private void updatePath() {
StringBuffer strBuffer = new StringBuffer("");
strBuffer.append(getName());
Item parent = getParent();
while (parent != null) {
strBuffer.insert(0, parent.getName() + " > ");
parent = parent.getParent();
}
this.path = strBuffer.toString();
}
/**
* @return Returns the path of this folder.
*/
public String getPath() {
if (this.path == null || this.path.length() == 0)
updatePath();
return this.path;
}
/**
* @param path
* The path to set.
*/
protected void setPath(String path) {
this.path = path;
}
public Item find(Long itemId) {
if (itemId.equals(getItemId()))
return this;
Item item = null;
List<Item> children = getChildren();
for (Item currentItem : children) {
if (currentItem.getItemId().equals(itemId)) {
item = currentItem;
break;
} else if (currentItem instanceof Folder) {
item = ((Folder) currentItem).find(itemId);
if (item != null)
break;
}
}
return item;
}
/**
* Returns the String representation of this Folder.
*/
@Override
public String toString() {
return super.toString() + "; path=" + this.getPath();
}
/**
*
* @return a list of Link objects that this Folder holds.
*/
@Transient
public List<Link> getLinks() {
List<Item> children = getChildren();
List<Link> links = new ArrayList<Link>(children.size()
- (children.size() / 2));
for (Item item : children) {
if (item instanceof Link) {
links.add((Link) item);
}
}
return links;
}
/**
* Returns the child Folders of this Folder and their child Folders, etc.
*
* @return
*/
@Transient
public List<Folder> getChildFolders() {
List<Folder> folderList = new ArrayList<Folder>();
buildFolderList(folderList, null);
folderList.remove(this);
return folderList;
}
public boolean isOpen() {
return open;
}
@Transient
public boolean isClosed() {
return !open;
}
public void setOpen(boolean open) {
this.open = open;
}
public Collection<Link> getOrderedLinks() {
return orderedLinks;
}
public void setOrderedLinks(Collection<Link> orderedLinks) {
this.orderedLinks = orderedLinks;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || (obj.getClass() != this.getClass())) {
return false;
}
Folder folder = (Folder) obj;
return this.getItemId() == folder.getItemId();
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int var = (int) (this.getItemId().longValue() ^ (this.getItemId()
.longValue() >>> 32));
int hash = 7;
hash = 31 * hash + var;
return hash;
}
}
@Entity
@DiscriminatorValue("L")
public class Link extends Item {
private String url;
public Link() {
}
public Link(String url) {
this.url = url;
}
/**
* @return Returns the url.
*/
public String getUrl() {
return url;
}
/**
* @param url
* The url to set.
*/
public void setUrl(String url) {
if (url != null && url.indexOf(":/") == -1)
url = "http://" + url;
this.url = url;
}
}
The Controller is calling the DAO, which calls entityManager.merge() (and I tried including entityManger.flush()). I also use OpenEntityInManagerInterceptor.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
由于您使用的是 TABLE 排序,因此 id 将在您的 persist() 调用上分配。您正在分离或序列化对象吗?您可能需要将持久分配的 id 返回给您的客户端。
Since you are using TABLE sequencing the id will be assign on your persist() call. Are you detaching or serializing the objects? You may need to return the id assigned in persist to your client.
尝试
保存新实体后 。这将强制 JPA 提供程序将您的持久性上下文与底层数据库同步,并且作为此过程的一部分,将生成任何 id。
Try
after saving the new Entity. This will force the JPA provider to synchronize your persistence context with the underlying database and as part of this any ids will be generated.