<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    gembin

    OSGi, Eclipse Equinox, ECF, Virgo, Gemini, Apache Felix, Karaf, Aires, Camel, Eclipse RCP

    HBase, Hadoop, ZooKeeper, Cassandra

    Flex4, AS3, Swiz framework, GraniteDS, BlazeDS etc.

    There is nothing that software can't fix. Unfortunately, there is also nothing that software can't completely fuck up. That gap is called talent.

    About Me

     

    JPA 學(xué)習(xí)

    Defining Your Object Model with JPA


    In the perfect world, your object model would map seamlessly to your database schema. Most organizations however, have database naming standards, requirements for how relationships are modeled and columns that all tables must have.

    The following example will show how to use JPA in a real-world scenario. You’ve been asked to quickly mock up a simple blog application; here are your constraints:

    • All database table names are uppercase, words separated by an underscore (“_”)
    • All table columns must start with the table name, or its initials
    • All mutable tables need to track who created and updated a row and when the insert and update occurred
    • Object relationships need to be managed intelligently
    • You should use as few trips as possible to the database to make sure we can handle current and future load

    The application should include the following objects:

    • User
    • Blog entry
    • Comments
    • The comments need to support nesting, so a user can reply to another’s comment, there is no limit to the nesting
    • You need to list all users who start comment threads, comments without responses, etc.

    Here is one possible object model:

    Figure 1: Blog Object Model

    ModelBase is an abstract superclass of three entities: BlogPost, Comment, and User. It also defines fields for who created and modified each entity and when the changes occurred. The nested comment requirement is taken care of by Comment’s parent and child relationships.

    The database schema for the object model is:

    Figure 2: Blog Entity Relationship Diagram

    The database tables can be seen here:

    The root object of the class hierarchy is ModeBase. The @Column annotation maps each field in ModelBase to a database table column. The problem is, ModelBase does not follow the database naming conventions listed above. This will be fixed with the concrete class definitions a little later. Here is ModelBase:

     1 import javax.persistence.*;
    2
    3 @MappedSuperclass
    4 @EntityListeners({ModelListener.class})
    5 public abstract class ModelBase {
    6
    7 @Id
    8 @GeneratedValue(strategy = GenerationType.IDENTITY)
    9 @Column(name="id")
    10 private Long id;
    11
    12 @Version
    13 @Column(name="version")
    14 private Integer version;
    15
    16 @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
    17 @JoinColumn(name="created_by_user_id")
    18 private User createdByUser;
    19
    20 @Temporal(value = TemporalType.TIMESTAMP)
    21 @Column(name = "date_created", nullable = false)
    22 private Date dateCreated;
    23
    24 @ManyToOne(fetch=FetchType.LAZY)
    25 @JoinColumn(name="updated_by_user_id")
    26 private User updatedByUser;
    27
    28 @Temporal(value = TemporalType.TIMESTAMP)
    29 @Column(name = "date_updated", nullable = false)
    30 private Date dateUpdated;
    31
    32 // methods removed for readability
    33 }

    ModelBase uses the @MappedSuperclass annotation to tell the JPA persistence provide that this object is not an entity but it’s fields will be included in each entities table (for the entities that subclass ModelBase). You can use a mapped superclass to define all common fields. In this case it defines a field for optimist locking, version, primary key, id and fields for date created and updated. The second annotation, @EntityListener defines a class to be called by the JPA persistence provider at various lifecycle events. ModelListener, sets the user who created and modified each entity and when it was created and updated. Here is ModelListener:

     1 public class ModelListener {
    2
    3 @PrePersist
    4 public void setDatesAndUser(ModelBase modelBase) {
    5
    6 // set createdBy and updatedBy User information
    7 User currentUser = UserUtil.getCurrentUser();
    8
    9 // check to see if modelBase and currentUser are
    10 // the same, if so, make currentUser modelBase.
    11 if (modelBase.equals(currentUser)) {
    12 currentUser = (User) modelBase;
    13 }
    14
    15 if (currentUser != null) {
    16 if (modelBase.getCreatedByUser() == null) {
    17 modelBase.setCreatedByUser(currentUser);
    18 }
    19 modelBase.setUpdatedByUser(currentUser);
    20 }
    21
    22 // set dateCreated and dateUpdated fields
    23 Date now = new Date();
    24 if (modelBase.getDateCreated() == null) {
    25 modelBase.setDateCreated(now);
    26 }
    27 modelBase.setDateUpdated(now);
    28 }
    29 }

    ModelListener has only one method, setDatesAndUser(ModelBase modelBase). This method is tied to the pre-persist entity lifecycle event and will always be called just before an entity is persisted to the database. This gives us a convenient way to set when and by whom the object is created and updated.

    JPA requires that entity listeners be stateless and therefore we need a way to establish who the current user of our system is so we can set the createdByUser and updatedByUser fields. The UserUtil class uses a ThreadLocal to store the current user and provides an easy way for clients to establish the “current” user of the system.

     1 public class UserUtil {
    2
    3 private static ThreadLocal<User> currentUser =
    4 new ThreadLocal<User>();
    5
    6 public static void setCurrentUser(User user) {
    7 currentUser.set(user);
    8 }
    9
    10 public static User getCurrentUser() {
    11 return currentUser.get();
    12 }
    13 }

    If you want to turn off any inherited entity listeners for a particular entity, you can use @ExcludeSuperclassListeners. This annotation does not have any elements, you add it to your entity like this:

    1 @ExcludeDefaultListeners
    2 public class Comment extends ModelBase implements Serializable

    Now, Comment will not be passed to ModelListener when it’s persisted.

    There are a couple of drawbacks to using entity listeners, most notably the lack of any lifecycle methods for the listener itself, it must be stateless, and it must have a public no-arg constructor. If you can live with these restrictions entity listeners are a good way to hook into the JPA entity lifecycle.

    JPA also supports callback methods, these are methods in your object model class itself and must have the following method signature: void methodName(). Use the entity lifecycle event annotations (e.g. @PrePersist or @PostPersist) to indicate which event the callback method participates in. Here is an example of a callback method (continuing with the example of Comment turning off it’s entity listeners):

     1 @ExcludeDefaultListeners
    2 public class Comment extends ModelBase implements Serializable {
    3
    4 // code removed for readability
    5
    6 @PrePersist
    7 public void setDates() {
    8 Date now = new Date();
    9 if (getDateCreated() == null) {
    10 setDateCreated(now);
    11 }
    12 setDateUpdated(now);
    13 }
    14 }

    By default, an entities table name is the same as the entity itself. In the case of BlogPost, the default table name would be BlogPost. To explicitly set the table name an entity is mapped to, use the @Table annotation. To change the column mappings defined in ModelBase, use @AttributeOverride. If you need to override more than one field, use the @AttributeOverrides (plural) annotation. Here is how you change the mappings:

    1 @Entity
    2 @Table(name = "BLOG_POST")
    3 @AttributeOverrides( { @AttributeOverride(name = "id", column = @Column(name = "BP_ID")),
    4 @AttributeOverride(name="version", column=@Column(name="BP_VERSION")),
    5 @AttributeOverride(name="dateCreated", column=@Column(name="BP_DATE_CREATED")),
    6 @AttributeOverride(name="dateUpdated", column=@Column(name="BP_DATE_UPDATED"))
    7 })
    8 public class BlogPost extends ModelBase implements Serializable {

    The two remaining fields to remap from ModelBase are not attributes but associations, so you need to use a different set of annotations, @AssociationOverrides (plural) and @AssociationOverride. Here is how you rename the createdByUser and updatedByUser foreign key columns in the BlogPost entity:

    1 @AssociationOverrides( {
    2 @AssociationOverride(name="createdByUser",
    3 joinColumns=@JoinColumn(name="BP_CREATED_BY_USER_ID")),
    4
    5 @AssociationOverride(name="updatedByUser",
    6 joinColumns=@JoinColumn(name="BP_UPDATED_BY_USER_ID"))
    7 })

    As you can see, the @AssociationOverride annotation is a little be more complex than @AttributeOverride because it has a nested annotation, @JoinColumn.

    As you can see in Figure 1, there are three more fields to be defined in BlogPost. Here is their definition:

    1     @Lob
    2 @Column(name = "BP_CONTENT")
    3 private String content;
    4
    5 @Column(name="BP_TITLE")
    6 private String title;
    7
    8 @Column(name = "BP_EDIT_COUNT")
    9 private Integer editCount;

    The @Lob annotation tells the persistence provider that content is bound to a large object type column. The @Column annotation defines the column name the content field is mapped to. Use the @Column annotation to map an entities field to a specific column name.

    One last thing to look at is BlogPost’s relationship to Comment. Here is how the comments field is defined:

    1     @OneToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE},
    2 fetch=FetchType.LAZY)
    3 @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
    4 private List<Comment> comments = new ArrayList<Comment>();

    The OneToMany annotation tells the persistence provider this is an association and not an attribute. The cascade element tells the persistence provider to persist or merge any comments associated with this instance when blog post is persisted or merged. Unfortunately, JPA does not provide a cascade type for managing orphaned deletes. Fortunately, the Hibernate team has created a set of Hibernate extension annotations to make managing relationships (and every other aspect of an entity) easier. The delete orphan cascade annotation lets Hibernate know it can delete a row from the comment table if a comment is removed from the comments list. Without this annotation, you would have to manually delete the removed comment.

    There are two ways for the persistence provider to map a one-to-many relationship in the database. One is to use a join table (a unidirectional relationship) and the other is to have a foreign key column in the many side of the relationship (a bidirectional relationship). Given the BlogPost to Comment relationship, if you just defined the blog post to comment relationship as shown above, JPA should expect the following tables in the database:

    Figure 3: Blog Post to Comment join table

    You eliminate the join table, BlogPost_Comment, by defining a bidirectional relationship. To tell JPA this is a bidirectional relationship, you need to add the mappedBy element to the @OneToMany annotation like this:

    1     @OneToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE},
    2 fetch=FetchType.LAZY, mappedBy="blogPost")
    3 @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
    4 private List<Comment> comments = new ArrayList<Comment>();

    The mappedBy element indicates the owning-side of the relationship, or the field in Comment that will hold a reference to BlogPost. Here is the comment side of the relationship:

    1     @ManyToOne
    2 @JoinColumn(name="COMMENT_BLOG_POST_ID")
    3 private BlogPost blogPost;

    By defining the post to comments relationship this way we eliminate the need for a join table.

    The Comment entity has the same attribute and association overrides as BlogPost (mapping to different column names of course), however it has an interesting relationship to itself. Comments support nested comments, so a comment can contain a list of responses to itself. To create this relationship, Comment needs two fields, one for the collection of comments and another to represent the parent comment. Here is how the fields are defined:

    1     @ManyToOne
    2 @JoinColumn(name="COMMENT_PARENT")
    3 private Comment parent;
    4
    5 @OneToMany(mappedBy="parent",
    6 cascade={CascadeType.MERGE, CascadeType.PERSIST})
    7 @OrderBy("dateCreated DESC")
    8 private List<Comment> children = new ArrayList<Comment>();

    These relationships are the same as any other one-to-many bidirectional relationship between BlogPost and Comment; the only difference is both sides of the relationship are in the same class. The parent field holds the primary key of the parent comment.

    Now that you have the fields defined, you need to add methods to manage the relationship. Both sides of the relationship need to be set in order for the Comment entity to behave properly. To ensure the relationship is set up correctly the java bean property methods for children need to be slightly modified. Since the JPA annotations used in the Comment entity are field based, the JPA persistence provider does not use property accessor methods to set its state. This means we do not need the public void setChildren(List<Comment> children) method for the persistence provider. In addition, by removing this method clients of our model cannot directly set the collection. Next is the public List<Comment> getChildren() method; like setChildren() this method allows clients to directly modify a comments children. To fix this, make getChildren() return an immutable list, like this:

    1     public List<Comment> getChildren() {
    2 return Collections.unmodifiableList(children);
    3 }

    The final step is to define methods to add and remove child comments. Here is the method for adding child comments:

     1     public void addChildComment(Comment comment) {
    2 if (comment == null) {
    3 throw new IllegalArgumentException("child comment is null!");
    4 }
    5
    6 // check to see if comment has a parent
    7 if (comment.getParent() != null) {
    8 // if there is a parent check to see if it's already
    9 // associated to this comment
    10 if (comment.getParent().equals(this)) {
    11 // if this instance is already the parent, we can just return
    12 return;
    13 }
    14 else {
    15 // disconnect post from it's current relationship
    16 comment.getParent().children.remove(this);
    17 }
    18 }
    19
    20 // make this instance the new parent
    21 comment.setParent(this);
    22 children.add(comment);
    23 }

    If you want to add a remove comment method, it might look like this:

     1     public void removeChildComment(Comment comment) {
    2 if (comment == null) {
    3 throw new IllegalArgumentException("child comment is null!");
    4 }
    5
    6 // make sure we are the parent before we break the relationship
    7 if (comment.parent != null && comment.getParent().equals(this)) {
    8 comment.setParent(null);
    9 children.remove(comment);
    10 }
    11 else {
    12 throw new IllegalArgumentException(
    13 "child comment not associated with this instance");
    14 }
    15 }

    Not only does this method remove a comment from its parents children collection, it makes sure the parent of comment is this instance. If comment were not associated with this instance, removing it from children would have no affect. However, setting its parent to null would leave a dangling comment in the database.

    The last two methods to deal with are getParent() and setParent() . Here is their definition:

    1     public Comment getParent() {
    2 return parent;
    3 }
    4
    5 private void setParent(Comment parent) {
    6 this.parent = parent;
    7 }

    The addChildComment() method uses setParent() , however; clients of the object model should not be able to change a comments parent. The easiest way to ensure this is to make setParent() private. getParent() is fine the way it is. The pattern used for Comments parent/child relationship can be applied to any bidirectional association in your object model.

    Deciding where to put the relationship management methods in a bidirectional relationship is rather arbitrary since the purpose of the methods is to ensure the relationships are established correctly. With one-to-many bidirectional relationships, I tend to put the management methods on the “one” side. In the case of Comment this isn’t obvious, but in the BlogPost to Comment relationship I placed the relationship methods in BlogPost. This seems more natural; you add comments to a blog post, not the other way around. If you have a many-to-many relationship it really doesn’t matter which side has the methods so pick one and restrict the other side.

    JPA provides methods for fetching entities by their primary key but it might be a good idea to provide some queries to fetch blog posts and comments by User, count the number of comments and blog posts a user has made, determine which comments do not have child comments, and find all root comments (those without a parent).

    JPA provides three different types of queries, dynamic queries, static or named queries, and native queries. Dynamic and static queries use the Java Persistence Query Language and native queries use SQL. A dynamic query is one that is processed at runtime, meaning it is parsed and SQL generated every time it’s created. Static queries are processed when the persistence provider loads your object model. This means static queries are parsed once and reused every time you run the query.

    To declare a named or static query, you use the @NamedQuery annotation. If you have more than one named query, you need to use the @NamedQueries annotation. Both annotations can be placed on an entity or mapped superclass and are declared at the class or type level. Query names are global, that is, they are not bound to any entity. As a result, you should use some sort of naming convention. One approach is to prefix every query name with the entity name it’s associated with.

    Here are the named queries used in Comment and BlogPost:

     1 @NamedQueries({
    2 // select comments that do not have parent comments
    3 @NamedQuery(name = "comment.rootComments",
    4 query = "SELECT c FROM Comment c WHERE c.parent IS NULL"),
    5
    6 // select comments made by a User
    7 @NamedQuery(name = "comment.userComments",
    8 query = "SELECT c FROM Comment c WHERE c.createdByUser = :user"),
    9
    10 // count the number of comments made by a user
    11 @NamedQuery(name = "comment.userCount",
    12 query = "SELECT COUNT(c) FROM Comment c WHERE c.createdByUser = :user"),
    13
    14 // select the comments a user made without responses
    15 @NamedQuery(name = "comment.noChildren",
    16 query = "SELECT c FROM Comment c WHERE c.children IS EMPTY AND c.parent IS NULL AND c.createdByUser = ?1")
    17 })
    18 public class Comment extends ModelBase implements Serializable{ ... }
    19
    20
    21 @NamedQueries( {
    22 @NamedQuery(name = "blogPost.createdByUser",
    23 query = "SELECT p FROM BlogPost p WHERE p.createdByUser = ?1"),
    24
    25 // determine the number of posts a User has made
    26 @NamedQuery(name = "blogPost.postCount",
    27 query = "SELECT COUNT(p) FROM BlogPost p WHERE p.createdByUser = ?1"),
    28
    29 // fetch a blog post and eagerly fetch its comments
    30 @NamedQuery(name = "blogPost.createdByUserComments",
    31 query = "SELECT p FROM BlogPost p JOIN FETCH p.comments as c WHERE p.createdByUser = ?1")
    32 })
    33 public class BlogPost extends ModelBase implements Serializable { ... }

    Each of the above queries uses either positional or named parameters; positional parameters use the “?1” syntax and named parameters use the “:name” syntax. Here is how you would create and execute a static query which uses named parameters:

    1 // assume entityManager exists
    2 User user = UserUtil.getCurrentUser();
    3
    4 // Here is the definition of comment.userComments for reference
    5 // SELECT c FROM Comment c WHERE c.createdByUser = :user
    6
    7 Query q = entityManager.createNamedQuery("comment.userComments");
    8 q.setParameter("user", user);
    9 List results = q.getResultList();

    Another query using positional parameters (note: the count function returns a Long):

    1 // assume entityManager exists
    2 User user = UserUtil.getCurrenstUser();
    3
    4 // Here is the definition of blogPost.postCount for reference
    5 // SELECT COUNT(p) FROM BlogPost p WHERE p.createdByUser = ?1
    6
    7 Query q = entityManager.createNamedQuery("blogPost.postCount");
    8 q.setParameter(1, user);
    9 Long result = (Long) q.getSingleResult();

    The JPQL has many built in functions and expressions to assist you in working with your object model. If you have not checked out JPQL before, you might be surprised by how powerful it is.

    The last query to look at is blogPost.createdByUserComments. This query uses the fetch join operator to eagerly fetch a blog posts’ comments. The BlogPost to Comment association is defined as being LAZY (here is the relationship again):

    1     @OneToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE},
    2 fetch=FetchType.LAZY, mappedBy="blogPost")
    3 @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
    4 private List<Comment> comments = new ArrayList<Comment>();

    Making the relationship LAZY (by setting the fetch element of the @OneToMany annotation to FetchType.LAZY) enables your application to fetch a blog post, say when a user wants to edit the post, without also fetching all the comments. When you want to fetch the blog post and its comments, you can use the named query, blogPost.createdByUserComments. Sometimes you will want to make a relationship eager, by setting the fetch element of the @OneToMany annotation to FetchType.EAGER, so when you fetch entity “A” the persistence provider will also fetch entity “B”. Having one fetch to get back several objects is more efficient than multiple trips to the database but you will need to decide the correct semantics for each collection in your object model.

    If you haven’t looked at JPA before, I hope this short example will encourage you to give it a try. Hibernate’s latest release has excellent JPA support and provides an extensive set of JPA extension annotations.

    Get the Source Code here.

    Biography

    Chris Maki is a Principal Software Engineer at Overstock.com. Before joining Overstock.com, Chris was an independent consultant specializing in server-side Java development. He has been designing and building server-side systems for 15 years in C++, Objective-C, and Java.

    Chris spent many years working as an enterprise architect specializing in large-scale Java system design and development. In addition to being a Java evangelist, he is an avid proponent of Agile Software Development.

    Chris is the President of the Utah Java User Group and a member of the WebBeans (JSR-299) and JPA 2.0 (JSR-317) expert groups.

    When Chris isn?t playing with his computer, you can find him hanging out with his wonderful wife of 12 years, Nicole, and their three boys, Trenton, Avery, and Brayden. You can find Chris on his blog at http://www.jroller.com/page/cmaki.

    posted on 2008-04-30 19:19 gembin 閱讀(2528) 評(píng)論(1)  編輯  收藏 所屬分類: JavaEE

    評(píng)論

    # re: JPA 學(xué)習(xí) 2010-06-18 11:29 lacewigs

    great site. Thanks for sharing with us!!!  回復(fù)  更多評(píng)論   

    導(dǎo)航

    統(tǒng)計(jì)

    常用鏈接

    留言簿(6)

    隨筆分類(440)

    隨筆檔案(378)

    文章檔案(6)

    新聞檔案(1)

    相冊

    收藏夾(9)

    Adobe

    Android

    AS3

    Blog-Links

    Build

    Design Pattern

    Eclipse

    Favorite Links

    Flickr

    Game Dev

    HBase

    Identity Management

    IT resources

    JEE

    Language

    OpenID

    OSGi

    SOA

    Version Control

    最新隨筆

    搜索

    積分與排名

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    free counters
    主站蜘蛛池模板: jjizz全部免费看片| 久久久久亚洲精品中文字幕| 久久综合图区亚洲综合图区| 亚洲av午夜国产精品无码中文字| 全部免费毛片在线播放| 亚洲精品视频久久久| 亚洲人成网站18禁止| 182tv免费观看在线视频| 国产成人综合亚洲AV第一页 | 最新亚洲人成无码网www电影| 91精品国产免费入口| 亚洲色欲久久久综合网| 豆国产96在线|亚洲| 青青视频观看免费99| 亚洲嫩草影院久久精品| 伊人久久大香线蕉免费视频| 国产成人免费a在线视频app| 亚洲毛片基地4455ww| 16女性下面扒开无遮挡免费| 久久精品国产亚洲综合色| 一级做受视频免费是看美女 | a毛片在线免费观看| 亚洲成a人一区二区三区| 亚洲精品无码高潮喷水A片软| 性短视频在线观看免费不卡流畅| 亚洲短视频男人的影院| a级毛片黄免费a级毛片| 亚洲色一色噜一噜噜噜| 免费国产黄网站在线观看动图| 青草草在线视频永久免费| 国产精品亚洲精品观看不卡| 免费黄色福利视频| 色播亚洲视频在线观看| 午夜无码A级毛片免费视频 | 黄色免费在线网站| 亚洲成a人片在线观看日本| a级成人毛片免费图片| 亚洲真人无码永久在线| 中文字幕a∨在线乱码免费看| 亚洲国产成人久久综合区| 免费精品国自产拍在线播放|