001package io.ebean; 002 003import io.ebean.bean.EntityBean; 004 005 006/** 007 * A MappedSuperclass base class that provides convenience methods for inserting, updating and 008 * deleting beans. 009 * <p> 010 * By having your entity beans extend this it provides a 'Active Record' style programming model for 011 * Ebean users. 012 * <p> 013 * Note that there is a ebean-mocker project that enables you to use Mockito or similar 014 * tools to still mock out the underlying 'default Database' for testing purposes. 015 * <p> 016 * You may choose not use this Model mapped superclass if you don't like the 'Active Record' style 017 * or if you believe it 'pollutes' your entity beans. 018 * <p> 019 * You can use Dependency Injection like Guice or Spring to construct and wire a Database instance 020 * and have that same instance used with this Model and Finder. The way that works is that when the 021 * DI container creates the Database instance it can be registered with DB. In this 022 * way the Database instance can be injected as per normal Guice / Spring dependency injection and 023 * that same instance also used to support the Model and Finder active record style. 024 * <p> 025 * If you choose to use the Model mapped superclass you will probably also chose to additionally add 026 * a {@link Finder} as a public static field to complete the active record pattern and provide a 027 * relatively nice clean way to write queries. 028 * <p> 029 * <h3>Typical common @MappedSuperclass</h3> 030 * <pre>{@code 031 * 032 * // Typically there is a common base model that has some 033 * // common properties like the ones below 034 * 035 * @MappedSuperclass 036 * public class BaseModel extends Model { 037 * 038 * @Id Long id; 039 * 040 * @Version Long version; 041 * 042 * @WhenCreated Timestamp whenCreated; 043 * 044 * @WhenUpdated Timestamp whenUpdated; 045 * 046 * ... 047 * } 048 * }</pre> 049 * <p> 050 * <h3>Extend the Model</h3> 051 * <pre>{@code 052 * 053 * // Extend the mappedSuperclass 054 * 055 * @Entity @Table(name="o_account") 056 * public class Customer extends BaseModel { 057 * 058 * String name; 059 * ... 060 * } 061 * 062 * }</pre> 063 * <p> 064 * <h3>Modal: save()</h3> 065 * <pre>{@code 066 * 067 * // Active record style ... save(), delete() etc 068 * Customer customer = new Customer(); 069 * customer.setName("AC234"); 070 * 071 * // save() method inherited from Model 072 * customer.save(); 073 * 074 * }</pre> 075 */ 076public abstract class Model { 077 078 /** 079 * The name of the database this entity will use, null for the default database. 080 */ 081 private final String _$dbName; 082 083 /** 084 * Create using the default database. 085 */ 086 public Model() { 087 this._$dbName = null; 088 } 089 090 /** 091 * Create with a named database (typically not the default database). 092 */ 093 public Model(String dbName) { 094 this._$dbName = dbName; 095 } 096 097 /** 098 * Return the underlying 'default' Database. 099 * <p> 100 * This provides full access to the API such as explicit transaction demarcation etc. 101 * <p> 102 * Example: 103 * <pre>{@code 104 * 105 * try (Transaction transaction = Customer.db().beginTransaction()) { 106 * 107 * // turn off cascade persist for this transaction 108 * transaction.setPersistCascade(false); 109 * 110 * // extra control over jdbc batching for this transaction 111 * transaction.setBatchGetGeneratedKeys(false); 112 * transaction.setBatchMode(true); 113 * transaction.setBatchSize(20); 114 * 115 * Customer customer = new Customer(); 116 * customer.setName("Roberto"); 117 * customer.save(); 118 * 119 * Customer otherCustomer = new Customer(); 120 * otherCustomer.setName("Franko"); 121 * otherCustomer.save(); 122 * 123 * transaction.commit(); 124 * 125 * } 126 * 127 * }</pre> 128 */ 129 public Database db() { 130 return DB.byName(_$dbName); 131 } 132 133 /** 134 * Marks the entity bean as dirty. 135 * <p> 136 * This is used so that when a bean that is otherwise unmodified is updated the version 137 * property is updated. 138 * <p> 139 * An unmodified bean that is saved or updated is normally skipped and this marks the bean as 140 * dirty so that it is not skipped. 141 * <p> 142 * <pre>{@code 143 * 144 * Customer customer = Customer.find.byId(id); 145 * 146 * // mark the bean as dirty so that a save() or update() will 147 * // increment the version property 148 * customer.markAsDirty(); 149 * customer.save(); 150 * 151 * }</pre> 152 * 153 * @see Database#markAsDirty(Object) 154 */ 155 public void markAsDirty() { 156 db().markAsDirty(this); 157 } 158 159 /** 160 * Mark the property as unset or 'not loaded'. 161 * <p> 162 * This would be used to specify a property that we did not wish to include in a stateless update. 163 * </p> 164 * <pre>{@code 165 * 166 * // populate an entity bean from JSON or whatever 167 * User user = ...; 168 * 169 * // mark the email property as 'unset' so that it is not 170 * // included in a 'stateless update' 171 * user.markPropertyUnset("email"); 172 * 173 * user.update(); 174 * 175 * }</pre> 176 * 177 * @param propertyName the name of the property on the bean to be marked as 'unset' 178 */ 179 public void markPropertyUnset(String propertyName) { 180 ((EntityBean) this)._ebean_getIntercept().setPropertyLoaded(propertyName, false); 181 } 182 183 /** 184 * Insert or update this entity depending on its state. 185 * <p> 186 * Ebean will detect if this is a new bean or a previously fetched bean and perform either an 187 * insert or an update based on that. 188 * 189 * @see Database#save(Object) 190 */ 191 public void save() { 192 db().save(this); 193 } 194 195 /** 196 * Save this entity with an explicit transaction. 197 */ 198 public void save(Transaction transaction) { 199 db().save(this, transaction); 200 } 201 202 /** 203 * Flush any batched changes to the database. 204 * <p> 205 * When using JDBC batch flushing occurs automatically at commit() time or when the batch size 206 * is reached. This provides the ability to manually flush the batch. 207 * </p> 208 */ 209 public void flush() { 210 db().flush(); 211 } 212 213 /** 214 * Update this entity. 215 * 216 * @see Database#update(Object) 217 */ 218 public void update() { 219 db().update(this); 220 } 221 222 /** 223 * Update this entity with an explicit transaction. 224 */ 225 public void update(Transaction transaction) { 226 db().update(this, transaction); 227 } 228 229 /** 230 * Insert this entity. 231 * 232 * @see Database#insert(Object) 233 */ 234 public void insert() { 235 db().insert(this); 236 } 237 238 /** 239 * Insert with an explicit transaction. 240 */ 241 public void insert(Transaction transaction) { 242 db().insert(this, transaction); 243 } 244 245 /** 246 * Delete this bean. 247 * <p> 248 * This will return true if the bean was deleted successfully or JDBC batch is being used. 249 * </p> 250 * <p> 251 * If there is no current transaction one will be created and committed for 252 * you automatically. 253 * </p> 254 * <p> 255 * If the Bean does not have a version property (or loaded version property) and 256 * the bean does not exist then this returns false indicating that nothing was 257 * deleted. Note that, if JDBC batch mode is used then this always returns true. 258 * </p> 259 * 260 * @see Database#delete(Object) 261 */ 262 public boolean delete() { 263 return db().delete(this); 264 } 265 266 /** 267 * Delete this entity with an explicit transaction. 268 */ 269 public boolean delete(Transaction transaction) { 270 return db().delete(this, transaction); 271 } 272 273 /** 274 * Delete a bean permanently without soft delete. 275 * <p> 276 * This is used when the bean contains a <code>@SoftDelete</code> property and we 277 * want to perform a hard/permanent delete. 278 * </p> 279 * 280 * @see Database#deletePermanent(Object) 281 */ 282 public boolean deletePermanent() { 283 return db().deletePermanent(this); 284 } 285 286 /** 287 * Delete a bean permanently without soft delete using an explicit transaction. 288 */ 289 public boolean deletePermanent(Transaction transaction) { 290 return db().deletePermanent(this, transaction); 291 } 292 293 /** 294 * Refreshes this entity from the database. 295 * 296 * @see Database#refresh(Object) 297 */ 298 public void refresh() { 299 db().refresh(this); 300 } 301 302}