From 67ff27496dd075e85640961ed5cc5445ba95eb0f Mon Sep 17 00:00:00 2001 From: Alex Huang Date: Wed, 25 May 2011 16:58:12 -0700 Subject: [PATCH] tags added --- server/src/com/cloud/network/NetworkVO.java | 21 ++- setup/db/create-schema.sql | 2 +- utils/src/com/cloud/utils/db/Attribute.java | 74 ++++++----- .../com/cloud/utils/db/GenericDaoBase.java | 125 ++++++++++++++++-- .../src/com/cloud/utils/db/SqlGenerator.java | 47 ++++--- .../javax/persistence/ElementCollection.java | 13 +- 6 files changed, 205 insertions(+), 77 deletions(-) diff --git a/server/src/com/cloud/network/NetworkVO.java b/server/src/com/cloud/network/NetworkVO.java index ab760faacfe..af60f5e27b1 100644 --- a/server/src/com/cloud/network/NetworkVO.java +++ b/server/src/com/cloud/network/NetworkVO.java @@ -22,11 +22,15 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import javax.persistence.CollectionTable; import javax.persistence.Column; +import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; +import javax.persistence.FetchType; import javax.persistence.Id; +import javax.persistence.JoinColumn; import javax.persistence.Table; import javax.persistence.TableGenerator; import javax.persistence.Transient; @@ -34,7 +38,6 @@ import javax.persistence.Transient; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.Mode; import com.cloud.network.Networks.TrafficType; -import com.cloud.network.dao.NetworkDao; import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.GenericDao; import com.cloud.utils.net.NetUtils; @@ -73,7 +76,7 @@ public class NetworkVO implements Network { String displayText;; @Column(name="broadcast_uri") - URI broadcastUri; + URI broadcastUri; @Column(name="gateway") String gateway; @@ -132,7 +135,7 @@ public class NetworkVO implements Network { Date created; @Column(name="reservation_id") - String reservationId; + String reservationId; @Column(name="is_default") boolean isDefault; @@ -140,7 +143,9 @@ public class NetworkVO implements Network { @Column(name="is_security_group_enabled") boolean securityGroupEnabled; - @Transient + @ElementCollection(targetClass = String.class, fetch=FetchType.EAGER) + @Column(name="tag") + @CollectionTable(name="network_tags", joinColumns=@JoinColumn(name="network_id")) List tags; public NetworkVO() { @@ -408,7 +413,7 @@ public class NetworkVO implements Network { return isDefault; } - @Override + @Override public boolean isSecurityGroupEnabled() { return securityGroupEnabled; } @@ -464,10 +469,4 @@ public class NetworkVO implements Network { buf.append(id).append("|").append(trafficType.toString()).append("|").append(networkOfferingId).append("]"); return buf.toString(); } - - private static NetworkDao _networkDao = null; - static void init(NetworkDao networkDao) { - _networkDao = networkDao; - } - } diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index 3b6ab9132be..ffa7a5e4db4 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -197,7 +197,7 @@ CREATE TABLE `cloud`.`network_tags` ( `network_id` bigint unsigned NOT NULL COMMENT 'id of the network', `tag` varchar(255) NOT NULL COMMENT 'tag', PRIMARY KEY (`id`), - CONSTRAINT `fk_network_tags__network_id` FOREIGN KEY (`network_id`) REFERENCES `networks`(`id`), + CONSTRAINT `fk_network_tags__network_id` FOREIGN KEY (`network_id`) REFERENCES `networks`(`id`) ON DELETE CASCADE, UNIQUE KEY(`network_id`, `tag`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/utils/src/com/cloud/utils/db/Attribute.java b/utils/src/com/cloud/utils/db/Attribute.java index aefb02dd139..ee13be3f42d 100755 --- a/utils/src/com/cloud/utils/db/Attribute.java +++ b/utils/src/com/cloud/utils/db/Attribute.java @@ -21,6 +21,7 @@ import java.lang.reflect.Field; import javax.persistence.AttributeOverride; import javax.persistence.Column; +import javax.persistence.ElementCollection; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @@ -54,49 +55,50 @@ public class Attribute { CharDT(0x100000), StringDT(0x200000), IntegerDT(0x400000); - + int place; Flag(int place) { this.place = place; } - + public int place() { return place; } - + public boolean check(int value) { return (value & place) == place; } - + public int setTrue(int value) { return (value | place); } - + public int setFalse(int value) { return (value & ~place); } } - + protected String table; protected String columnName; protected Field field; protected int flags; protected Column column; - + protected Object attache; + public Attribute(Class clazz, AttributeOverride[] overrides, Field field, String tableName, boolean isEmbedded, boolean isId) { this.field = field; flags = 0; table = tableName; setupColumnInfo(clazz, overrides, tableName, isEmbedded, isId); } - + public Attribute(String table, String columnName) { this.table = table; this.columnName = columnName; this.field = null; this.column = null; } - + protected void setupColumnInfo(Class clazz, AttributeOverride[] overrides, String tableName, boolean isEmbedded, boolean isId) { flags = Flag.Selectable.setTrue(flags); GeneratedValue gv = field.getAnnotation(GeneratedValue.class); @@ -122,7 +124,7 @@ public class Attribute { if (isEmbedded) { flags = Flag.Embedded.setTrue(flags); } - + if (isId) { flags = Flag.Id.setTrue(flags); } else { @@ -143,6 +145,12 @@ public class Attribute { flags = Flag.Nullable.setTrue(flags); } } + ElementCollection ec = field.getAnnotation(ElementCollection.class); + if (ec != null) { + flags = Flag.Insertable.setFalse(flags); + flags = Flag.Selectable.setFalse(flags); + } + Temporal temporal = field.getAnnotation(Temporal.class); if (temporal != null) { if (temporal.value() == TemporalType.DATE) { @@ -153,50 +161,50 @@ public class Attribute { flags = Flag.TimeStamp.setTrue(flags); } } - + if (column != null && column.table().length() > 0) { table = column.table(); } - + columnName = DbUtil.getColumnName(field, overrides); } - + public final boolean isInsertable() { return Flag.Insertable.check(flags); } - + public final boolean isUpdatable() { return Flag.Updatable.check(flags); } - + public final boolean isNullable() { return Flag.Nullable.check(flags); } - + public final boolean isId() { return Flag.Id.check(flags); } - + public final boolean isSelectable() { return Flag.Selectable.check(flags); } - + public final boolean is(Flag flag) { return flag.check(flags); } - + public final void setTrue(Flag flag) { flags = flag.setTrue(flags); } - + public final void setFalse(Flag flag) { flags = flag.setFalse(flags); } - + public Field getField() { return field; } - + public Object get(Object entity) { try { return field.get(entity); @@ -205,23 +213,23 @@ public class Attribute { return null; } } - + @Override public int hashCode() { - return columnName.hashCode(); + return columnName.hashCode(); } - + @Override public boolean equals(Object obj) { - if (!(obj instanceof Attribute)) { - return false; - } - - Attribute that = (Attribute)obj; - - return columnName.equals(that.columnName) && table.equals(that.table); + if (!(obj instanceof Attribute)) { + return false; + } + + Attribute that = (Attribute)obj; + + return columnName.equals(that.columnName) && table.equals(that.table); } - + @Override public String toString() { return table + "." + columnName; diff --git a/utils/src/com/cloud/utils/db/GenericDaoBase.java b/utils/src/com/cloud/utils/db/GenericDaoBase.java index 1be42c4f5d9..a350e8d8fd4 100755 --- a/utils/src/com/cloud/utils/db/GenericDaoBase.java +++ b/utils/src/com/cloud/utils/db/GenericDaoBase.java @@ -19,6 +19,7 @@ package com.cloud.utils.db; import java.io.Serializable; import java.io.UnsupportedEncodingException; +import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -36,6 +37,7 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Date; +import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -49,7 +51,6 @@ import javax.persistence.EmbeddedId; import javax.persistence.EntityExistsException; import javax.persistence.EnumType; import javax.persistence.Enumerated; -import javax.persistence.OneToMany; import javax.persistence.TableGenerator; import net.sf.cglib.proxy.Callback; @@ -72,6 +73,8 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.Ip; import com.cloud.utils.net.NetUtils; +import edu.emory.mathcs.backport.java.util.Collections; + /** * GenericDaoBase is a simple way to implement DAOs. It DOES NOT * support the full EJB3 spec. It borrows some of the annotations from @@ -135,7 +138,7 @@ public abstract class GenericDaoBase implements Gene protected Map _idAttributes; protected Map _tgs; protected final Map _allAttributes; - protected List _oneToManyAttributes; + protected List _ecAttributes; protected final Map, Attribute> _allColumns; protected Enhancer _enhancer; protected Factory _factory; @@ -224,7 +227,7 @@ public abstract class GenericDaoBase implements Gene _deleteSqls = generator.buildDeleteSqls(); _removed = generator.getRemovedAttribute(); _tgs = generator.getTableGenerators(); - _oneToManyAttributes = generator.getOneToManyAttributes(); + _ecAttributes = generator.getElementCollectionAttributes(); TableGenerator tg = this.getClass().getAnnotation(TableGenerator.class); if (tg != null) { @@ -261,6 +264,13 @@ public abstract class GenericDaoBase implements Gene for (final Pair deletSql : _deleteSqls) { s_logger.trace(deletSql.first()); } + + s_logger.trace("Collection SQLs"); + for (Attribute attr : _ecAttributes) { + EcInfo info = (EcInfo)attr.attache; + s_logger.trace(info.insertSql); + s_logger.trace(info.selectSql); + } } } @@ -610,7 +620,7 @@ public abstract class GenericDaoBase implements Gene return (M)new String(bytes, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new CloudRuntimeException("UnsupportedEncodingException exception while converting UTF-8 data"); - } + } } else { return null; } @@ -767,7 +777,7 @@ public abstract class GenericDaoBase implements Gene } final String sqlStr = pstmt.toString(); throw new CloudRuntimeException("DB Exception on: " + sqlStr, e); - } + } } @DB(txn=false) @@ -892,8 +902,8 @@ public abstract class GenericDaoBase implements Gene ResultSet rs = pstmt.executeQuery(); return rs.next() ? toEntityBean(rs, true) : null; } catch (SQLException e) { - throw new CloudRuntimeException("DB Exception on: " + pstmt.toString(), e); - } + throw new CloudRuntimeException("DB Exception on: " + pstmt, e); + } } @Override @DB(txn=false) @@ -1212,6 +1222,30 @@ public abstract class GenericDaoBase implements Gene } } } + for (Attribute attr : _ecAttributes) { + EcInfo ec = (EcInfo)attr.attache; + Object obj; + try { + obj = attr.field.get(entity); + if (ec.rawClass != null) { + Enumeration en = Collections.enumeration((Collection)obj); + while (en.hasMoreElements()) { + pstmt = txn.prepareAutoCloseStatement(ec.insertSql); + if (ec.targetClass == Date.class) { + pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), (Date)en.nextElement())); + } else { + pstmt.setObject(1, en.nextElement()); + } + prepareAttribute(2, pstmt, _idAttributes.get(attr.table)[0], _idField.get(entity)); + pstmt.executeUpdate(); + } + } + } catch (IllegalArgumentException e) { + throw new CloudRuntimeException("Yikes! ", e); + } catch (IllegalAccessException e) { + throw new CloudRuntimeException("Yikes! ", e); + } + } txn.commit(); } catch (final SQLException e) { if (e.getSQLState().equals("23000") && e.getErrorCode() == 1062) { @@ -1235,7 +1269,7 @@ public abstract class GenericDaoBase implements Gene } else if (attr.is(Attribute.Flag.AutoGV)) { if (attr.columnName.equals(GenericDao.XID_COLUMN)) { return UUID.randomUUID().toString(); - } + } assert (false) : "Auto generation is not supported."; return null; } else if (attr.is(Attribute.Flag.SequenceGV)) { @@ -1268,7 +1302,7 @@ public abstract class GenericDaoBase implements Gene final Column column = attr.field.getAnnotation(Column.class); final int length = column != null ? column.length() : 255; - // to support generic localization, utilize MySql UTF-8 support + // to support generic localization, utilize MySql UTF-8 support if (length < str.length()) { try { pstmt.setBytes(j, str.substring(0, column.length()).getBytes("UTF-8")); @@ -1379,9 +1413,78 @@ public abstract class GenericDaoBase implements Gene for (int index = 1, max = meta.getColumnCount(); index <= max; index++) { setField(entity, result, meta, index); } - for (Attribute attr : _oneToManyAttributes) { - OneToMany otm = attr.field.getAnnotation(OneToMany.class); + for (Attribute attr : _ecAttributes) { + loadCollection(entity, attr); + } + } + @DB(txn = true) + @SuppressWarnings("unchecked") + protected void loadCollection(T entity, Attribute attr) { + EcInfo ec = (EcInfo)attr.attache; + + Transaction txn = Transaction.currentTxn(); + PreparedStatement pstmt = null; + try { + pstmt = txn.prepareAutoCloseStatement(ec.selectSql); + pstmt.setObject(1, _idField.get(entity)); + ResultSet rs = pstmt.executeQuery(); + ArrayList lst = new ArrayList(); + if (ec.targetClass == Integer.class) { + while (rs.next()) { + lst.add(rs.getInt(1)); + } + } else if (ec.targetClass == Long.class) { + while (rs.next()) { + lst.add(rs.getLong(1)); + } + } else if (ec.targetClass == String.class) { + while (rs.next()) { + lst.add(rs.getString(1)); + } + } else if (ec.targetClass == Short.class) { + while (rs.next()) { + lst.add(rs.getShort(1)); + } + } else if (ec.targetClass == Date.class) { + while (rs.next()) { + lst.add(DateUtil.parseDateString(s_gmtTimeZone, rs.getString(1))); + } + } else if (ec.targetClass == Boolean.class) { + while (rs.next()) { + lst.add(rs.getBoolean(1)); + } + } else { + assert (false) : "You'll need to add more classeses"; + } + + if (ec.rawClass == null) { + Object[] array = (Object[])Array.newInstance(ec.targetClass); + lst.toArray(array); + try { + attr.field.set(entity, array); + } catch (IllegalArgumentException e) { + throw new CloudRuntimeException("Come on we screen for this stuff, don't we?", e); + } catch (IllegalAccessException e) { + throw new CloudRuntimeException("Come on we screen for this stuff, don't we?", e); + } + } else { + try { + Collection coll = (Collection)ec.rawClass.newInstance(); + coll.addAll(lst); + attr.field.set(entity, coll); + } catch (IllegalAccessException e) { + throw new CloudRuntimeException("Come on we screen for this stuff, don't we?", e); + } catch (InstantiationException e) { + throw new CloudRuntimeException("Never should happen", e); + } + } + } catch (SQLException e) { + throw new CloudRuntimeException("Error executing " + pstmt, e); + } catch (IllegalArgumentException e) { + throw new CloudRuntimeException("Error executing " + pstmt, e); + } catch (IllegalAccessException e) { + throw new CloudRuntimeException("Error executing " + pstmt, e); } } diff --git a/utils/src/com/cloud/utils/db/SqlGenerator.java b/utils/src/com/cloud/utils/db/SqlGenerator.java index 9928dc1caec..d8be5c5e259 100755 --- a/utils/src/com/cloud/utils/db/SqlGenerator.java +++ b/utils/src/com/cloud/utils/db/SqlGenerator.java @@ -29,16 +29,16 @@ import java.util.List; import java.util.Map; import javax.persistence.AttributeOverride; -import javax.persistence.CascadeType; +import javax.persistence.CollectionTable; import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorType; import javax.persistence.DiscriminatorValue; +import javax.persistence.ElementCollection; import javax.persistence.Embeddable; import javax.persistence.Embedded; import javax.persistence.EmbeddedId; import javax.persistence.Entity; import javax.persistence.FetchType; -import javax.persistence.OneToMany; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.SecondaryTable; import javax.persistence.TableGenerator; @@ -54,13 +54,13 @@ public class SqlGenerator { ArrayList> _tables; LinkedHashMap> _ids; HashMap _generators; - ArrayList _otmAttrs; + ArrayList _ecAttrs; public SqlGenerator(Class clazz) { _clazz = clazz; _tables = new ArrayList>(); _attributes = new ArrayList(); - _otmAttrs = new ArrayList(); + _ecAttrs = new ArrayList(); _embeddeds = new ArrayList(); _ids = new LinkedHashMap>(); _generators = new HashMap(); @@ -68,6 +68,7 @@ public class SqlGenerator { buildAttributes(clazz, DbUtil.getTableName(clazz), DbUtil.getAttributeOverrides(clazz), false, false); assert (_tables.size() > 0) : "Did you forget to put @Entity on " + clazz.getName(); handleDaoAttributes(clazz); + findEcAttributes(); } protected boolean checkMethods(Class clazz, Map attrs) { @@ -140,27 +141,33 @@ public class SqlGenerator { attrs.add(attr); } - if (field.getAnnotation(OneToMany.class) != null) { - assert supportsOneToMany(field) : "Doesn't support One To Many"; - _otmAttrs.add(attr); - } else { - _attributes.add(attr); + _attributes.add(attr); + } + } + + protected void findEcAttributes() { + for (Attribute attr : _attributes) { + ElementCollection ec = attr.field.getAnnotation(ElementCollection.class); + if (ec != null) { + Attribute idAttr = _ids.get(attr.table).get(0); + assert supportsElementCollection(attr.field) : "Doesn't support ElementCollection for " + attr.field.getName(); + attr.attache = new EcInfo(attr, idAttr); + _ecAttrs.add(attr); } } } - protected boolean supportsOneToMany(Field field) { - OneToMany otm = field.getAnnotation(OneToMany.class); + protected boolean supportsElementCollection(Field field) { + ElementCollection otm = field.getAnnotation(ElementCollection.class); if (otm.fetch() == FetchType.LAZY) { - assert (false) : "Doesn't support laz fetch: " + field.getName(); - return false; + assert (false) : "Doesn't support laz fetch: " + field.getName(); + return false; } - for (CascadeType cascade : otm.cascade()) { - if (cascade == CascadeType.ALL || cascade == CascadeType.PERSIST || cascade == CascadeType.MERGE || cascade == CascadeType.REMOVE) { - assert (false) : "Doesn't support " + cascade + " for " + field.getName(); - return false; - } + CollectionTable ct = field.getAnnotation(CollectionTable.class); + if (ct == null) { + assert (false) : "No collection table sepcified for " + field.getName(); + return false; } return true; @@ -264,8 +271,8 @@ public class SqlGenerator { } } - public List getOneToManyAttributes() { - return _otmAttrs; + public List getElementCollectionAttributes() { + return _ecAttrs; } public Attribute findAttribute(String name) { diff --git a/utils/src/javax/persistence/ElementCollection.java b/utils/src/javax/persistence/ElementCollection.java index aaaa96edf25..622bab2fba8 100644 --- a/utils/src/javax/persistence/ElementCollection.java +++ b/utils/src/javax/persistence/ElementCollection.java @@ -17,6 +17,17 @@ */ package javax.persistence; -public interface ElementCollection { +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(value = { METHOD, FIELD }) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface ElementCollection { + FetchType fetch() default FetchType.LAZY; + + Class targetClass() default void.class; }