1. Trang chủ
  2. » Công Nghệ Thông Tin

Java Persistence with Hibernate phần 3 pot

87 465 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 87
Dung lượng 817,28 KB

Nội dung

Alternative entity representation 141 ■ MAP —No Java classes are required; entities are represented in the Java appli- cation with HashMap s. This mode allows quick prototyping of fully dynamic applications. ■ DOM4J —No Java classes are required; entities are represented as XML ele- ments, based on the dom4j API. This mode is especially useful for exporting or importing data, or for rendering and transforming data through XSLT processing. There are two reasons why you may want to skip the next section and come back later: First, a static domain model implementation with POJOs is the common case, and dynamic or XML representation are features you may not need right now. Second, we’re going to present some mappings, queries, and other opera- tions that you may not have seen so far, not even with the default POJO entity mode. However, if you feel confident enough with Hibernate, read on. Let’s start with the MAP mode and explore how a Hibernate application can be fully dynamically typed. 3.4.1 Creating dynamic applications A dynamic domain model is a model that is dynamically typed. For example, instead of a Java class that represents an auction item, you work with a bunch of values in a Java Map . Each attribute of an auction item is represented by a key (the name of the attribute) and its value. Mapping entity names First, you need to enable this strategy by naming your business entities. In a Hiber- nate XML mapping file, you use the entity-name attribute: <hibernate-mapping> <class entity-name="ItemEntity" table="ITEM_ENTITY"> <id name="id" type="long" column="ITEM_ID"> <generator class="native"/> </id> <property name="initialPrice" type="big_decimal" column="INIT_PRICE"/> <property name="description" type="string" column="DESCRIPTION"/> <many-to-one name="seller" entity-name="UserEntity" column="USER_ID"/> 142 CHAPTER 3 Domain models and metadata </class> <class entity-name="UserEntity" table="USER_ENTITY"> <id name="id" type="long" column="USER_ID"> <generator class="native"/> </id> <property name="username" type="string" column="USERNAME"/> <bag name="itemsForSale" inverse="true" cascade="all"> <key column="USER_ID"/> <one-to-many entity-name="ItemEntity"/> </bag> </class> </hibernate-mapping> There are three interesting things to observe in this mapping file. First, you mix several class mappings in one, something we didn’t recommend earlier. This time you aren’t really mapping Java classes, but logical names of enti- ties. You don’t have a Java source file and an XML mapping file with the same name next to each other, so you’re free to organize your metadata in any way you like. Second, the <class name=" "> attribute has been replaced with <class entity-name=" "> . You also append Entity to these logical names for clar- ity and to distinguish them from other nondynamic mappings that you made ear- lier with regular POJOs. Finally, all entity associations, such as <many-to-one> and <one-to-many> , now also refer to logical entity names. The class attribute in the association mappings is now entity-name . This isn’t strictly necessary—Hibernate can recognize that you’re referring to a logical entity name even if you use the class attribute. How- ever, it avoids confusion when you later mix several representations. Let’s see what working with dynamic entities looks like. Working with dynamic maps To create an instance of one of your entities, you set all attribute values in a Java Map : Map user = new HashMap(); user.put("username", "johndoe"); Map item1 = new HashMap(); item1.put("description", "An item for auction"); item1.put("initialPrice", new BigDecimal(99)); item1.put("seller", user); Alternative entity representation 143 Map item2 = new HashMap(); item2.put("description", "Another item for auction"); item2.put("initialPrice", new BigDecimal(123)); item2.put("seller", user); Collection itemsForSale = new ArrayList(); itemsForSale.add(item1); itemsForSale.add(item2); user.put("itemsForSale", itemsForSale); session.save("UserEntity", user); The first map is a UserEntity , and you set the username attribute as a key/value pair. The next two maps are ItemEntity s, and here you set the link to the seller of each item by putting the user map into the item1 and item2 maps. You’re effectively linking maps—that’s why this representation strategy is sometimes also called “representation with maps of maps.” The collection on the inverse side of the one-to-many association is initialized with an ArrayList , because you mapped it with bag semantics (Java doesn’t have a bag implementation, but the Collection interface has bag semantics). Finally, the save() method on the Session is given a logical entity name and the user map as an input parameter. Hibernate knows that UserEntity refers to the dynamically mapped entity, and that it should treat the input as a map that has to be saved accordingly. Hiber- nate also cascades to all elements in the itemsForSale collection; hence, all item maps are also made persistent. One UserEntity and two ItemEntity s are inserted into their respective tables. FAQ Can I map a Set in dynamic mode? Collections based on sets don’t work with dynamic entity mode. In the previous code example, imagine that itemsForSale was a Set . A Set checks its elements for duplicates, so when you call add(item1) and add(item2) , the equals() method on these objects is called. However, item1 and item2 are Java Map instances, and the equals() implementation of a map is based on the key sets of the map. So, because both item1 and item2 are maps with the same keys, they aren’t distinct when added to a Set . Use bags or lists only if you require collections in dynamic entity mode. Hibernate handles maps just like POJO instances. For example, making a map per- sistent triggers identifier assignment; each map in persistent state has an identifier attribute set with the generated value. Furthermore, persistent maps are automat- ically checked for any modifications inside a unit of work. To set a new price on an item, for example, you can load it and then let Hibernate do all the work: 144 CHAPTER 3 Domain models and metadata Long storedItemId = (Long) item1.get("id"); Session session = getSessionFactory().openSession(); session.beginTransaction(); Map loadedItemMap = (Map) session.load("ItemEntity", storedItemId); loadedItemMap.put("initialPrice", new BigDecimal(100)); session.getTransaction().commit(); session.close(); All Session methods that have class parameters such as load() also come in an overloaded variation that accepts entity names. After loading an item map, you set a new price and make the modification persistent by committing the transaction, which, by default, triggers dirty checking and flushing of the Session . You can also refer to entity names in HQL queries: List queriedItemMaps = session.createQuery("from ItemEntity where initialPrice >= :p") .setParameter("p", new BigDecimal(100)) .list(); This query returns a collection of ItemEntity maps. They are in persistent state. Let’s take this one step further and mix a POJO model with dynamic maps. There are two reasons why you would want to mix a static implementation of your domain model with a dynamic map representation: ■ You want to work with a static model based on POJO classes by default, but sometimes you want to represent data easily as maps of maps. This can be particularly useful in reporting, or whenever you have to implement a generic user interface that can represent various entities dynamically. ■ You want to map a single POJO class of your model to several tables and then select the table at runtime by specifying a logical entity name. You may find other use cases for mixed entity modes, but they’re so rare that we want to focus on the most obvious. First, therefore, you’ll mix a static POJO model and enable dynamic map repre- sentation for some of the entities, some of the time. Mixing dynamic and static entity modes To enable a mixed model representation, edit your XML mapping metadata and declare a POJO class name and a logical entity name: <hibernate-mapping> <class name="model.ItemPojo" entity-name="ItemEntity" Alternative entity representation 145 table="ITEM_ENTITY"> <many-to-one name="seller" entity-name="UserEntity" column="USER_ID"/> </class> <class name="model.UserPojo" entity-name="UserEntity" table="USER_ENTITY"> <bag name="itemsForSale" inverse="true" cascade="all"> <key column="USER_ID"/> <one-to-many entity-name="ItemEntity"/> </bag> </class> </hibernate-mapping> Obviously, you also need the two classes, model.ItemPojo and model.UserPojo , that implement the properties of these entities. You still base the many-to-one and one-to-many associations between the two entities on logical names. Hibernate will primarily use the logical names from now on. For example, the following code does not work: UserPojo user = new UserPojo(); ItemPojo item1 = new ItemPojo(); ItemPojo item2 = new ItemPojo(); Collection itemsForSale = new ArrayList(); session.save(user); The preceding example creates a few objects, sets their properties, and links them, and then tries to save the objects through cascading by passing the user instance to save() . Hibernate inspects the type of this object and tries to figure out what entity it is, and because Hibernate now exclusively relies on logical entity names, it can’t find a mapping for model.UserPojo . You need to tell Hibernate the logical name when working with a mixed representation mapping: session.save("UserEntity", user); Once you change this line, the previous code example works. Next, consider loading, and what is returned by queries. By default, a particular SessionFactory 146 CHAPTER 3 Domain models and metadata is in POJO entity mode, so the following operations return instances of model.ItemPojo : Long storedItemId = item1.getId(); ItemPojo loadedItemPojo = (ItemPojo) session.load("ItemEntity", storedItemId); List queriedItemPojos = session.createQuery("from ItemEntity where initialPrice >= :p") .setParameter("p", new BigDecimal(100)) .list(); You can switch to a dynamic map representation either globally or temporarily, but a global switch of the entity mode has serious consequences. To switch globally, add the following to your Hibernate configuration; e.g., in hibernate.cfg.xml : <property name="default_entity_mode">dynamic-map</property> All Session operations now either expect or return dynamically typed maps! The previous code examples that stored, loaded, and queried POJO instances no longer work; you need to store and load maps. It’s more likely that you want to switch to another entity mode temporarily, so let’s assume that you leave the SessionFactory in the default POJO mode. To switch to dynamic maps in a particular Session , you can open up a new tempo- rary Session on top of the existing one. The following code uses such a tempo- rary Session to store a new auction item for an existing seller: Session dynamicSession = session.getSession(EntityMode.MAP); Map seller = (Map) dynamicSession.load("UserEntity", user.getId() ); Map newItemMap = new HashMap(); newItemMap.put("description", "An item for auction"); newItemMap.put("initialPrice", new BigDecimal(99)); newItemMap.put("seller", seller); dynamicSession.save("ItemEntity", newItemMap); Long storedItemId = (Long) newItemMap.get("id"); Map loadedItemMap = (Map) dynamicSession.load("ItemEntity", storedItemId); List queriedItemMaps = dynamicSession .createQuery("from ItemEntity where initialPrice >= :p") .setParameter("p", new BigDecimal(100)) .list(); The temporary dynamicSession that is opened with getSession() doesn’t need to be flushed or closed; it inherits the context of the original Session . You use it Alternative entity representation 147 only to load, query, or save data in the chosen representation, which is the Entity- Mode.MAP in the previous example. Note that you can’t link a map with a POJO instance; the seller reference has to be a HashMap , not an instance of UserPojo . We mentioned that another good use case for logical entity names is the map- ping of one POJO to several tables, so let’s look at that. Mapping a class several times Imagine that you have several tables with some columns in common. For exam- ple, you could have ITEM_AUCTION and ITEM_SALE tables. Usually you map each table to an entity persistent class, ItemAuction and ItemSale respectively. With the help of entity names, you can save work and implement a single persistent class. To map both tables to a single persistent class, use different entity names (and usually different property mappings): <hibernate-mapping> <class name="model.Item" entity-name="ItemAuction" table="ITEM_AUCTION"> <id name="id" column="ITEM_AUCTION_ID"> </id> <property name="description" column="DESCRIPTION"/> <property name="initialPrice" column="INIT_PRICE"/> </class> <class name="model.Item" entity-name="ItemSale" table="ITEM_SALE"> <id name="id" column="ITEM_SALE_ID"> </id> <property name="description" column="DESCRIPTION"/> <property name="salesPrice" column="SALES_PRICE"/> </class> </hibernate-mapping> The model.Item persistent class has all the properties you mapped: id , descrip- tion , initialPrice , and salesPrice . Depending on the entity name you use at runtime, some properties are considered persistent and others transient: Item itemForAuction = new Item(); itemForAuction.setDescription("An item for auction"); itemForAuction.setInitialPrice( new BigDecimal(99) ); session.save("ItemAuction", itemForAuction); Item itemForSale = new Item(); itemForSale.setDescription("An item for sale"); 148 CHAPTER 3 Domain models and metadata itemForSale.setSalesPrice( new BigDecimal(123) ); session.save("ItemSale", itemForSale); Thanks to the logical entity name, Hibernate knows into which table it should insert the data. Depending on the entity name you use for loading and querying entities, Hibernate selects from the appropriate table. Scenarios in which you need this functionality are rare, and you’ll probably agree with us that the previous use case isn’t good or common. In the next section, we introduce the third built-in Hibernate entity mode, the representation of domain entities as XML documents. 3.4.2 Representing data in XML XML is nothing but a text file format; it has no inherent capabilities that qualify it as a medium for data storage or data management. The XML data model is weak, its type system is complex and underpowered, its data integrity is almost com- pletely procedural, and it introduces hierarchical data structures that were out- dated decades ago. However, data in XML format is attractive to work with in Java; we have nice tools. For example, we can transform XML data with XSLT, which we consider one of the best use cases. Hibernate has no built-in functionality to store data in an XML format; it relies on a relational representation and SQL, and the benefits of this strategy should be clear. On the other hand, Hibernate can load and present data to the application developer in an XML format. This allows you to use a sophisticated set of tools without any additional transformation steps. Let’s assume that you work in default POJO mode and that you quickly want to obtain some data represented in XML. Open a temporary Session with the Enti- tyMode.DOM4J : Session dom4jSession = session.getSession(EntityMode.DOM4J); Element userXML = (Element) dom4jSession.load(User.class, storedUserId); What is returned here is a dom4j Element , and you can use the dom4j API to read and manipulate it. For example, you can pretty-print it to your console with the following snippet: try { OutputFormat format = OutputFormat.createPrettyPrint(); XMLWriter writer = new XMLWriter( System.out, format); writer.write( userXML ); } catch (IOException ex) { throw new RuntimeException(ex); } Alternative entity representation 149 If we assume that you reuse the POJO classes and data from the previous exam- ples, you see one User instance and two Item instances (for clarity, we no longer name them UserPojo and ItemPojo ): <User> <id>1</id> <username>johndoe</username> <itemsForSale> <Item> <id>2</id> <initialPrice>99</initialPrice> <description>An item for auction</description> <seller>1</seller> </Item> <Item> <id>3</id> <initialPrice>123</initialPrice> <description>Another item for auction</description> <seller>1</seller> </Item> </itemsForSale> </User> Hibernate assumes default XML element names—the entity and property names. You can also see that collection elements are embedded, and that circular refer- ences are resolved through identifiers (the <seller> element). You can change this default XML representation by adding node attributes to your Hibernate mapping metadata: <hibernate-mapping> <class name="Item" table="ITEM_ENTITY" node="item"> <id name="id" type="long" column="ITEM_ID" node="@id"> <generator class="native"/> </id> <property name="initialPrice" type="big_decimal" column="INIT_PRICE" node="item-details/@initial-price"/> <property name="description" type="string" column="DESCRIPTION" node="item-details/@description"/> <many-to-one name="seller" class="User" column="USER_ID" embed-xml="false" 150 CHAPTER 3 Domain models and metadata node="@seller-id"/> </class> <class name="User" table="USERS" node="user"> <id name="id" type="long" column="USER_ID" node="@id"> <generator class="native"/> </id> <property name="username" type="string" column="USERNAME" node="@username"/> <bag name="itemsForSale" inverse="true" cascade="all" embed-xml="true" node="items-for-sale"> <key column="USER_ID"/> <one-to-many class="Item"/> </bag> </class> </hibernate-mapping> Each node attribute defines the XML representation: ■ A node="name" attribute on a <class> mapping defines the name of the XML element for that entity. ■ A node="name" attribute on any property mapping specifies that the prop- erty content should be represented as the text of an XML element of the given name. ■ A node="@name" attribute on any property mapping specifies that the prop- erty content should be represented as an XML attribute value of the given name. ■ A node="name/@attname" attribute on any property mapping specifies that the property content should be represented as an XML attribute value of the given name, on a child element of the given name. The embed-xml option is used to trigger embedding or referencing of associated entity data. The updated mapping results in the following XML representation of the same data you’ve seen before: <user id="1" username="johndoe"> <items-for-sale> <item id="2" seller-id="1"> <item-details initial-price="99" description="An item for auction"/> </item> [...]... 3. 1 summarizes the differences between Hibernate and Java Persistence related to concepts discussed in this chapter Table 3. 1 Hibernate and JPA comparison chart for chapter 3 Hibernate Core Java Persistence and EJB 3. 0 Persistent classes require a no-argument constructor with public or protected visibility if proxybased lazy loading is used The JPA specification mandates a no-argument constructor with. .. accessor methods, but not both if full portability is required Summary Table 3. 1 1 53 Hibernate and JPA comparison chart for chapter 3 (continued) Hibernate Core Java Persistence and EJB 3. 0 The XML metadata format supports all possible Hibernate mapping options JPA annotations cover all basic and most advanced mapping options Hibernate Annotations are required for exotic mappings and tuning XML mapping... names in backticks, to force Hibernate to use quoted identifiers everywhere You should consider renaming tables or columns with reserved keyword names whenever possible Quoting with backticks works with annotation mappings, but it’s an implementation detail of Hibernate and not part of the JPA specification 4 .3. 6 Implementing naming conventions We often encounter organizations with strict conventions for... in the memory space of the virtual machine (If you’re a Java guru, we acknowledge that String is a special case Assume we used a different class to make the same point.) Persistence complicates this picture With object/relational persistence, a persistent object is an in-memory representation of a particular row of a database table Along with Java identity (memory location) and object equality, you... direct annotation is available: @Entity @org .hibernate. annotations.GenericGenerator( name = "hibernate- uuid", strategy = "uuid" ) class name MyEntity { @Id @GeneratedValue(generator = "hibernate- uuid") @Column(name = "MY_ID") String id; } The @GenericGenerator Hibernate extension can be used to give a Hibernate identifier generator a name, in this case hibernate- uuid This name is then referenced by... by Hibernate for immutable persistent classes, so you don’t have to write useless accessor methods You can map an immutable entity using annotations: @Entity @org .hibernate. annotations.Entity(mutable = false) @org .hibernate. annotations.AccessType("field") public class Bid { Again, the native Hibernate @Entity annotation extends the JPA annotation with additional options We have also shown the Hibernate. .. Chapter 4 starts with regular class and property mappings, and explains how you can map fine-grained Java domain models Next, in chapter 5, you’ll see how to map more complex class inheritance hierarchies and how to extend Hibernate' s functionality with the powerful custom mapping type system In chapters 6 and 7, we show you how to map Java collections and associations between classes, with many sophisticated... strategy, like the native option in XML Hibernate mappings Note that if you don’t define a strategy, the default is also Generation- Mapping entities with identity 165 Type.AUTO, so you could have omitted this attribute altogether You also specify a database column—otherwise Hibernate would use the property name The mapping type is implied by the Java property type, java. lang.Long Of course, you can also... values, so it isn’t supported with user-supplied database connections In other words, don’t use it with Options Description sessionFactory.openSession(myCo nnection) The max_lo option defines how many low values are added until a new high value is fetched Only settings greater than 1 are sensible; the default is 32 767 (Short.MAX_VALUE) Mapping entities with identity Table 4.1 169 Hibernate s built-in identifier-generator... another issue, the naming of entities for queries 4 .3. 3 Naming entities for querying By default, all class names are automatically “imported” into the namespace of the Hibernate query language, HQL In other words, you can use the short class names without a package prefix in HQL, which is convenient However, this autoimport can be turned off if two classes with the same name exist for a given SessionFactory, . convenience, table 3. 1 summarizes the differences between Hibernate and Java Persistence related to concepts discussed in this chapter. Table 3. 1 Hibernate and JPA comparison chart for chapter 3 Hibernate. the standard orm.xml metadata file. Table 3. 1 Hibernate and JPA comparison chart for chapter 3 (continued) Hibernate Core Java Persistence and EJB 3. 0 Part 2 Mapping concepts and strategies This. seen so far, not even with the default POJO entity mode. However, if you feel confident enough with Hibernate, read on. Let’s start with the MAP mode and explore how a Hibernate application

Ngày đăng: 12/08/2014, 19:21

TỪ KHÓA LIÊN QUAN