2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.j2ee.persistence.wizard.fromdb;
44
import com.sun.source.tree.*;
45
import java.util.HashMap;
46
import org.netbeans.api.progress.aggregate.ProgressContributor;
47
import org.netbeans.modules.j2ee.persistence.api.metadata.orm.Entity;
48
import org.netbeans.modules.j2ee.persistence.api.metadata.orm.Table;
49
import org.netbeans.spi.java.classpath.ClassPathProvider;
50
import org.openide.filesystems.FileObject;
51
import java.io.IOException;
52
import java.util.ArrayList;
53
import java.util.Collections;
54
import java.util.EnumSet;
55
import java.util.HashSet;
56
import java.util.Iterator;
57
import java.util.List;
60
import java.util.logging.Level;
61
import java.util.logging.Logger;
62
import javax.lang.model.element.*;
63
import javax.lang.model.type.*;
64
import org.netbeans.api.java.classpath.ClassPath;
65
import org.netbeans.api.java.source.JavaSource;
66
import org.netbeans.api.java.source.TreeMaker;
67
import org.netbeans.api.java.source.WorkingCopy;
68
import org.netbeans.api.project.FileOwnerQuery;
69
import org.netbeans.api.project.Project;
70
import org.netbeans.modules.j2ee.persistence.util.AbstractTask;
71
import org.netbeans.modules.j2ee.persistence.util.GenerationUtils;
72
import org.netbeans.modules.j2ee.persistence.dd.persistence.model_1_0.PersistenceUnit;
73
import org.netbeans.modules.j2ee.persistence.entitygenerator.CMPMappingModel;
74
import org.netbeans.modules.j2ee.persistence.entitygenerator.EntityClass;
75
import org.netbeans.modules.j2ee.persistence.entitygenerator.EntityMember;
76
import org.netbeans.modules.j2ee.persistence.entitygenerator.RelationshipRole;
77
import org.netbeans.modules.j2ee.persistence.provider.InvalidPersistenceXmlException;
78
import org.netbeans.modules.j2ee.persistence.provider.ProviderUtil;
79
import org.netbeans.modules.j2ee.persistence.unit.PUDataObject;
80
import org.netbeans.modules.j2ee.persistence.util.EntityMethodGenerator;
81
import org.netbeans.modules.j2ee.persistence.util.JPAClassPathHelper;
82
import org.netbeans.modules.j2ee.persistence.wizard.Util;
83
import org.openide.WizardDescriptor;
84
import org.openide.filesystems.FileUtil;
85
import org.openide.util.NbBundle;
88
* Generator of Java Persistence API ORM classes from DB.
90
* @author Pavel Buzek, Andrei Badea
92
public class JavaPersistenceGenerator implements PersistenceGenerator {
94
// XXX Javadoc for generated code missing in many places - issue 90302
95
// XXX createToStringMethod() could be moved to GenerationUtils
96
// XXX init() commented out until annotation model is implemented
98
// XXX empty lines in generated hashCode() - issue 90186
99
// XXX comments are lost in method body passed as string - issue 89873
100
// XXX return 0, 1 in generated equals() - issue 90183
101
// XXX empty line in generated equals() - issue 90186
103
private final Map<String, String> entityName2TableName = new HashMap<String, String>();
105
// options (not currently exposed in UI)
106
// field vs. property access
107
private static boolean fieldAccess = true;
108
// named input params for named queries vs. positional params
109
private static boolean genNamedParams = true;
110
// should generated Entity Classes implement Serializable?
111
private static boolean genSerializableEntities = true;
113
private Set<FileObject> result;
116
* Specifies whether the generated enties should be added to the first
117
* persistence unit found in the project. Note that this setting only
118
* applies to non-Java EE 5 projects - for Java EE 5 projects the entities
119
* are not added to a PU even if this is true.
121
private final boolean addToAutoDiscoveredPU;
124
* The persistence unit to which the generated entities should
127
private PersistenceUnit persistenceUnit;
130
* Creates a new instance of JavaPersistenceGenerator. Tries to add the
131
* generated entities to the first persistence unit found (only in non-Java EE 5 projects).
133
public JavaPersistenceGenerator() {
134
this.persistenceUnit = null;
135
this.addToAutoDiscoveredPU = true;
140
* Creates a new instance of JavaPersistenceGenerator
142
* @param persistenceUnit the persistence unit to which the generated entities
143
* should be added. Must exist in the project where the entities are generated.
144
* Has no effect in Java EE 5 projects - in those
145
* the entities are not added to any persistence unit regardless of this. May
146
* be null, in which case the generated entities are not added any persistence unit.
148
public JavaPersistenceGenerator(PersistenceUnit persistenceUnit) {
149
this.persistenceUnit = persistenceUnit;
150
this.addToAutoDiscoveredPU = false;
154
public void generateBeans(final ProgressPanel progressPanel,
155
final RelatedCMPHelper helper,
156
final FileObject dbSchemaFile,
157
final ProgressContributor handle) throws IOException {
159
generateBeans(helper.getBeans(), helper.isGenerateFinderMethods(), handle, progressPanel);
162
// package private for tests
163
void generateBeans(EntityClass[] entityClasses,
164
boolean generateNamedQueries, ProgressContributor progressContributor, ProgressPanel panel) throws IOException {
166
int progressMax = entityClasses.length * 2;
167
progressContributor.start(progressMax);
168
result = new Generator(entityClasses, generateNamedQueries, progressContributor, panel).run();
169
addToPersistenceUnit(result);
170
progressContributor.progress(progressMax);
175
* Adds the given entities to out persistence unit found in the project.
177
private void addToPersistenceUnit(Set<FileObject> entities){
179
if (entities.isEmpty()){
183
if (persistenceUnit == null && !addToAutoDiscoveredPU){
187
Project project = FileOwnerQuery.getOwner(entities.iterator().next());
188
if (project != null && !Util.isSupportedJavaEEVersion(project) && ProviderUtil.getDDFile(project) != null) {
190
PUDataObject pudo = ProviderUtil.getPUDataObject(project);
191
// no persistence unit was provider, we'll try find one
192
if (persistenceUnit == null){
193
PersistenceUnit pu[] = pudo.getPersistence().getPersistenceUnit();
194
//only add if a PU exists, if there are more we do not know where to add - UI needed to ask
195
if (pu.length == 1) {
196
persistenceUnit = pu[0];
199
if (persistenceUnit != null){
200
ClassPathProvider classPathProvider = project.getLookup().lookup(ClassPathProvider.class);
201
if (classPathProvider != null) {
202
for(FileObject entity : entities){
203
String entityFQN = classPathProvider.findClassPath(entity, ClassPath.SOURCE).getResourceName(entity, '.', false);
204
pudo.addClass(persistenceUnit, entityFQN);
209
} catch (InvalidPersistenceXmlException ipx){
210
// just log for debugging purposes, at this point the user has
211
// already been warned about an invalid persistence.xml
212
Logger.getLogger(JavaPersistenceGenerator.class.getName()).log(Level.FINE, "Invalid persistence.xml: " + ipx.getPath(), ipx); //NO18N
218
public void init(WizardDescriptor wiz) {
219
// get the table names for all entities in the project
220
// Project project = Templates.getProject(wiz);
222
// processEntities(PersistenceUtils.getEntityClasses(project));
223
// } catch (IOException e) {
224
// ErrorManager.getDefault().notify(e);
226
// processEntities(PersistenceUtils.getAnnotationEntityClasses(project));
229
private void processEntities(Set<Entity> entityClasses) {
230
for (Entity entity : entityClasses) {
231
Table entityTable = entity.getTable();
232
if (entityTable != null) {
233
entityName2TableName.put(entityTable.getName(), entity.getClass2());
238
public void uninit() {
241
public String getFQClassName(String tableName) {
242
return entityName2TableName.get(tableName);
245
public String generateEntityName(String name) {
249
public Set<FileObject> createdObjects() {
255
* Encapsulates the whole entity class generation process.
257
private static final class Generator {
258
private final ProgressPanel progressPanel;
259
private final ProgressContributor progressContributor;
260
private final Map<String, EntityClass> beanMap = new HashMap<String, EntityClass>();
261
private final EntityClass[] entityClasses;
262
private final boolean generateNamedQueries;
263
private final Set<FileObject> generatedEntityFOs;
264
private final Set<FileObject> generatedFOs;
266
public Generator(EntityClass[] entityClasses, boolean generateNamedQueries,
267
ProgressContributor progressContributor, ProgressPanel progressPanel) {
268
this.entityClasses = entityClasses;
269
this.generateNamedQueries = generateNamedQueries;
270
this.progressContributor = progressContributor;
271
this.progressPanel = progressPanel;
272
generatedFOs = new HashSet<FileObject>();
273
generatedEntityFOs = new HashSet<FileObject>();
276
public Set<FileObject> run() throws IOException {
279
} catch (IOException e) {
280
for (FileObject generatedFO : generatedFOs) {
281
generatedFO.delete();
285
return generatedEntityFOs;
288
public void runImpl() throws IOException {
290
// first generate empty entity classes -- this is needed as
291
// in the field and method generation it will be necessary to resolve
292
// their types (e.g. entity A has a field of type Collection<B>, thus
293
// while generating entity A we must be able to resolve type B).
296
Set<FileObject> generationPackageFOs = new HashSet<FileObject>();
297
Set<String> generatedEntityClasses = new HashSet<String>();
299
for (int i = 0; i < entityClasses.length; i++) {
300
final EntityClass entityClass = entityClasses[i];
301
String entityClassName = entityClass.getClassName();
302
FileObject packageFileObject = entityClass.getPackageFileObject();
303
beanMap.put(entityClassName, entityClass);
305
if (packageFileObject.getFileObject(entityClassName, "java") != null) { // NOI18N
306
progressContributor.progress(i);
309
String progressMsg = NbBundle.getMessage(JavaPersistenceGenerator.class, "TXT_GeneratingClass", entityClassName);
311
progressContributor.progress(progressMsg, i);
312
if (progressPanel != null){
313
progressPanel.setText(progressMsg);
316
generationPackageFOs.add(packageFileObject);
317
generatedEntityClasses.add(entityClassName);
320
FileObject entity = GenerationUtils.createClass(packageFileObject, entityClassName, NbBundle.getMessage(JavaPersistenceGenerator.class, "MSG_Javadoc_Class"));
321
generatedEntityFOs.add(entity);
322
generatedFOs.add(entity);
323
if (!entityClass.isUsePkField()) {
324
String pkClassName = createPKClassName(entityClassName);
325
if (packageFileObject.getFileObject(pkClassName, "java") == null) { // NOI18N
326
FileObject pkClass = GenerationUtils.createClass(packageFileObject, pkClassName, NbBundle.getMessage(JavaPersistenceGenerator.class, "MSG_Javadoc_PKClass", pkClassName, entityClassName));
327
generatedFOs.add(pkClass);
332
// now generate the fields and methods for each entity class
333
// and its primary key class
336
for (int i = 0; i < entityClasses.length; i++) {
337
final EntityClass entityClass = entityClasses[i];
338
String entityClassName = entityClass.getClassName();
340
if (!generatedEntityClasses.contains(entityClassName)) {
341
// this entity class already existed, we didn't create it, so we don't want to touch it
342
progressContributor.progress(entityClasses.length + i);
345
String progressMsg = NbBundle.getMessage(JavaPersistenceGenerator.class, "TXT_GeneratingClass", entityClassName);
346
progressContributor.progress(progressMsg, entityClasses.length + i);
347
if (progressPanel != null){
348
progressPanel.setText(progressMsg);
350
FileObject entityClassPackageFO = entityClass.getPackageFileObject();
351
final FileObject entityClassFO = entityClassPackageFO.getFileObject(entityClassName, "java"); // NOI18N
352
final FileObject pkClassFO = entityClassPackageFO.getFileObject(createPKClassName(entityClassName), "java"); // NOI18N
355
Set<ClassPath> bootCPs = getAllClassPaths(generationPackageFOs, ClassPath.BOOT);
356
Set<ClassPath> compileCPs = getAllClassPaths(generationPackageFOs, ClassPath.COMPILE);
357
Set<ClassPath> sourceCPs = getAllClassPaths(generationPackageFOs, ClassPath.SOURCE);
359
JPAClassPathHelper cpHelper = new JPAClassPathHelper(bootCPs, compileCPs, sourceCPs);
361
JavaSource javaSource = (pkClassFO != null) ?
362
JavaSource.create(cpHelper.createClasspathInfo(), entityClassFO, pkClassFO) :
363
JavaSource.create(cpHelper.createClasspathInfo(), entityClassFO);
364
javaSource.runModificationTask(new AbstractTask<WorkingCopy>() {
365
public void run(WorkingCopy copy) throws IOException {
366
if (copy.getFileObject().equals(entityClassFO)) {
367
new EntityClassGenerator(copy, entityClass, generateNamedQueries).run();
369
new PKClassGenerator(copy, entityClass).run();
373
} catch (IOException e) {
374
String message = e.getMessage();
375
String newMessage = ((message == null) ?
376
NbBundle.getMessage(JavaPersistenceGenerator.class, "ERR_GeneratingClass_NoExceptionMessage", entityClassName) :
377
NbBundle.getMessage(JavaPersistenceGenerator.class, "ERR_GeneratingClass", entityClassName, message));
378
IOException wrappedException = new IOException(newMessage);
379
wrappedException.initCause(e);
380
throw wrappedException;
387
private static String createPKClassName(String entityClassName) {
388
return entityClassName + "PK"; // NOI18N
391
private static Set<ClassPath> getAllClassPaths(Set<FileObject> fileObjects, String id) {
392
Set<ClassPath> classPaths = new HashSet<ClassPath>();
393
for (FileObject fileObject : fileObjects) {
394
classPaths.add(ClassPath.getClassPath(fileObject, id));
401
* Encapsulates common logic for generating classes (be it
402
* entity or primary key classes). Each instance generates a single
405
private abstract class ClassGenerator {
407
protected final WorkingCopy copy;
408
protected final GenerationUtils genUtils;
410
// the entity class we are generating
411
protected final EntityClass entityClass;
412
// the mapping of the entity class to the database
413
protected final CMPMappingModel dbMappings;
414
// true if a primary key class needs to be generated along with the entity class
415
protected final boolean needsPKClass;
416
// the simple class name of the primary key class
417
protected final String pkClassName;
418
// the fully-qualified name of the primary key class
419
protected final String pkFQClassName;
421
// generated properties
422
protected final List<Property> properties = new ArrayList<Property>();
424
protected final List<MethodTree> methods = new ArrayList<MethodTree>();
425
// generated constructors
426
protected final List<MethodTree> constructors = new ArrayList<MethodTree>();
427
// generated fields. does not include fields of properties, just plain fields
428
protected final List<VariableTree> fields = new ArrayList<VariableTree>();
430
// the class tree of the class we are generating
431
protected ClassTree classTree;
433
public ClassGenerator(WorkingCopy copy, EntityClass entityClass) throws IOException {
436
this.entityClass = entityClass;
437
dbMappings = entityClass.getCMPMapping();
438
needsPKClass = !entityClass.isUsePkField();
439
pkClassName = needsPKClass ? createPKClassName(entityClass.getClassName()) : null;
440
pkFQClassName = entityClass.getPackage() + "." + pkClassName; // NOI18N
443
genUtils = GenerationUtils.newInstance(copy);
444
if (genUtils == null) {
445
throw new IllegalStateException("Cannot find a public top-level class named " + entityClass.getClassName() + // NOI18N
446
" in " + FileUtil.getFileDisplayName(copy.getFileObject())); // NOI18N
448
classTree = genUtils.getClassTree();
451
protected String createFieldName(String capitalizedFieldName) {
452
return createFieldNameImpl(capitalizedFieldName, false);
455
protected String createCapitalizedFieldName(String fieldName) {
456
return createFieldNameImpl(fieldName, true);
459
private String createFieldNameImpl(String fieldName, boolean capitalized) {
460
StringBuffer sb = new StringBuffer(fieldName);
461
char firstChar = sb.charAt(0);
462
sb.setCharAt(0, capitalized ? Character.toUpperCase(firstChar) : Character.toLowerCase(firstChar));
463
return sb.toString();
467
* Creates a property for an entity member, that is, is creates
468
* a field, a getter and a setter method.
470
protected Property createProperty(EntityMember m) throws IOException {
471
boolean isPKMember = m.isPrimaryKey();
472
List<AnnotationTree> annotations = new ArrayList<AnnotationTree>();
474
//add @Id() only if not in an embeddable PK class
475
if (isPKMember && !needsPKClass) {
476
annotations.add(genUtils.createAnnotation("javax.persistence.Id")); // NOI18N
479
boolean isLobType = m.isLobType();
481
annotations.add(genUtils.createAnnotation("javax.persistence.Lob")); // NOI18N
484
List<ExpressionTree> columnAnnArguments = new ArrayList();
485
String memberName = m.getMemberName();
487
String columnName = (String) dbMappings.getCMPFieldMapping().get(memberName);
488
columnAnnArguments.add(genUtils.createAnnotationArgument("name", columnName)); //NOI18N
489
if (!m.isNullable()) {
490
columnAnnArguments.add(genUtils.createAnnotationArgument("nullable", false)); //NOI18N
492
annotations.add(genUtils.createAnnotation("javax.persistence.Column", columnAnnArguments)); //NOI18N
494
String temporalType = getMemberTemporalType(m);
495
if (temporalType != null) {
496
ExpressionTree temporalAnnValueArgument = genUtils.createAnnotationArgument(null, "javax.persistence.TemporalType", temporalType); //NOI18N
497
annotations.add(genUtils.createAnnotation("javax.persistence.Temporal", Collections.singletonList(temporalAnnValueArgument)));
500
return new Property(Modifier.PRIVATE, annotations, getMemberType(m), memberName);
504
* Like {@link #createProperty}, but it only creates a variable
505
* with no modififers and no annotations. Useful to pass in
506
* a parameter list when creating a method or constructor.
508
protected VariableTree createVariable(EntityMember m) {
509
return genUtils.createVariable(m.getMemberName(), getMemberType(m));
512
private String getMemberType(EntityMember m) {
513
String memberType = m.getMemberType();
514
if ("java.sql.Date".equals(memberType)) { //NOI18N
515
memberType = "java.util.Date";
516
} else if ("java.sql.Time".equals(memberType)) { //NOI18N
517
memberType = "java.util.Date";
518
} else if ("java.sql.Timestamp".equals(memberType)) { //NOI18N
519
memberType = "java.util.Date";
524
private String getMemberTemporalType(EntityMember m) {
525
String memberType = m.getMemberType();
526
String temporalType = null;
527
if ("java.sql.Date".equals(memberType)) { //NOI18N
528
temporalType = "DATE";
529
} else if ("java.sql.Time".equals(memberType)) { //NOI18N
530
temporalType = "TIME";
531
} else if ("java.sql.Timestamp".equals(memberType)) { //NOI18N
532
temporalType = "TIMESTAMP";
537
public void run() throws IOException {
539
for (Object object : entityClass.getFields()) {
540
generateMember((EntityMember)object);
542
afterMembersGenerated();
543
for (Object object : entityClass.getRoles()) {
544
generateRelationship((RelationshipRole)object);
548
// add the generated members
549
TreeMaker make = copy.getTreeMaker();
551
for (VariableTree field : fields){
552
classTree = make.insertClassMember(classTree, position, field);
555
for (Property property : properties) {
556
classTree = make.insertClassMember(classTree, position, property.getField());
559
for (MethodTree constructor : constructors) {
560
classTree = make.addClassMember(classTree, constructor);
562
for (Property property : properties) {
563
classTree = make.addClassMember(classTree, property.getGetter());
564
classTree = make.addClassMember(classTree, property.getSetter());
566
for (MethodTree method : methods) {
567
classTree = make.addClassMember(classTree, method);
569
copy.rewrite(genUtils.getClassTree(), classTree);
573
* Called at the beginning of the generation process.
575
protected abstract void initialize() throws IOException;
578
* Called for each entity class member.
580
protected abstract void generateMember(EntityMember m) throws IOException;
583
* Called after all members have been generated.
585
protected abstract void afterMembersGenerated() throws IOException;
588
* Called for each relationship.
590
protected abstract void generateRelationship(RelationshipRole role) throws IOException;
593
* Called at the end of the generation process.
595
protected abstract void finish() throws IOException;
598
* Encapsulates a generated property, that is, its field, getter
601
protected final class Property {
603
private final VariableTree field;
604
private final MethodTree getter;
605
private final MethodTree setter;
607
public Property(Modifier modifier, List<AnnotationTree> annotations, String type, String name) throws IOException {
608
this(modifier, annotations, genUtils.createType(type), name);
611
public Property(Modifier modifier, List<AnnotationTree> annotations, TypeMirror type, String name) throws IOException {
612
this(modifier, annotations, copy.getTreeMaker().Type(type), name);
615
private Property(Modifier modifier, List<AnnotationTree> annotations, Tree typeTree, String name) throws IOException {
616
TreeMaker make = copy.getTreeMaker();
617
field = make.Variable(
618
make.Modifiers(EnumSet.of(modifier), fieldAccess ? annotations : Collections.<AnnotationTree>emptyList()),
622
getter = genUtils.createPropertyGetterMethod(
623
make.Modifiers(EnumSet.of(Modifier.PUBLIC), fieldAccess ? Collections.<AnnotationTree>emptyList() : annotations),
626
setter = genUtils.createPropertySetterMethod(
627
genUtils.createModifiers(Modifier.PUBLIC),
632
public VariableTree getField() {
636
public MethodTree getGetter() {
640
public MethodTree getSetter() {
647
* An implementation of ClassGenerator which generates entity classes.
649
private final class EntityClassGenerator extends ClassGenerator {
651
// the simple name of the entity class
652
private final String entityClassName;
653
// the fully-qualified name of the entity class
654
private final String entityFQClassName;
655
// the non-nullable properties (not including the primary key ones)
656
private final List<Property> nonNullableProps = new ArrayList<Property>();
657
// the names of the primary key columns
658
private final List<String> pkColumnNames = new ArrayList<String>();
659
// variables correspoding to the fields in the primary key classs (or empty if no primary key class)
660
private final List<VariableTree> pkClassVariables = new ArrayList<VariableTree>();
661
// the list of @NamedQuery annotations which will be added to the entity class
662
private final List<ExpressionTree> namedQueryAnnotations = new ArrayList<ExpressionTree>();
664
* Specifies whether named queries should be generated.
666
private final boolean generateNamedQueries;
668
// the property for the primary key (or the primary key class)
669
private Property pkProperty;
670
// the prefix or all named queries ("select ... ")
671
private String namedQueryPrefix;
675
public EntityClassGenerator(WorkingCopy copy, EntityClass entityClass, boolean generateNamedQueries) throws IOException {
676
super(copy, entityClass);
677
this.generateNamedQueries = generateNamedQueries;
678
entityClassName = entityClass.getClassName();
679
assert genUtils.getTypeElement().getSimpleName().contentEquals(entityClassName);
680
entityFQClassName = entityClass.getPackage() + "." + entityClassName;
683
protected void initialize() throws IOException {
684
classTree = genUtils.ensureNoArgConstructor(classTree);
685
if (genSerializableEntities) {
686
classTree = genUtils.addImplementsClause(classTree, "java.io.Serializable"); // NOI18N
688
classTree = genUtils.addAnnotation(classTree, genUtils.createAnnotation("javax.persistence.Entity")); // NOI18N
689
ExpressionTree tableNameArgument = genUtils.createAnnotationArgument("name", dbMappings.getTableName()); // NOI18N
690
classTree = genUtils.addAnnotation(classTree, genUtils.createAnnotation("javax.persistence.Table", Collections.singletonList(tableNameArgument)));
693
String pkFieldName = createFieldName(pkClassName);
694
pkProperty = new Property(
696
Collections.singletonList(genUtils.createAnnotation("javax.persistence.EmbeddedId")),
699
properties.add(pkProperty);
702
//TODO: javadoc - generate or fake in test mode
703
// b.setCommentDataAuthor(authorOverride);
704
// b.setCommentDataDate(dateOverride);
707
protected void generateMember(EntityMember m) throws IOException {
708
String memberName = m.getMemberName();
709
boolean isPKMember = m.isPrimaryKey();
710
Property property = null;
713
pkClassVariables.add(createVariable(m));
715
pkProperty = property = createProperty(m);
717
String pkColumnName = (String)dbMappings.getCMPFieldMapping().get(memberName);
718
pkColumnNames.add(pkColumnName);
720
property = createProperty(m);
721
if (!m.isNullable()) {
722
nonNullableProps.add(property);
725
// we don't create the property only if the current member is
726
// part of a primary key, in which case it will be put in the primary key class
727
assert (property != null) || (property == null && isPKMember && needsPKClass);
728
if (property != null) {
729
properties.add(property);
732
// generate equivalent of finder methods - named query annotations
733
if (generateNamedQueries && !m.isLobType()) {
734
List<ExpressionTree> namedQueryAnnArguments = new ArrayList<ExpressionTree>();
735
namedQueryAnnArguments.add(genUtils.createAnnotationArgument("name", entityClassName + ".findBy" + createCapitalizedFieldName(memberName))); //NOI18N
737
if (namedQueryPrefix == null) {
738
char firstLetter = entityClassName.toLowerCase().charAt(0);
739
namedQueryPrefix = "SELECT " + firstLetter + " FROM " + entityClassName + " " + firstLetter + " WHERE " + firstLetter + "."; // NOI18N
741
// need a prefix of "pk_field_name." if this is part of a composite pk
742
String memberAccessString = ((needsPKClass && isPKMember) ? (pkProperty.getField().getName().toString() + "." + memberName) : memberName); // NOI18N
743
namedQueryAnnArguments.add(genUtils.createAnnotationArgument(
744
"query", namedQueryPrefix + //NOI18N
745
memberAccessString + ((genNamedParams) ? (" = :" + memberName) : "= ?1"))); //NOI18N
746
namedQueryAnnotations.add(genUtils.createAnnotation("javax.persistence.NamedQuery", namedQueryAnnArguments)); //NOI18N
750
protected void afterMembersGenerated() {
751
classTree = genUtils.addAnnotation(classTree, genUtils.createAnnotation("javax.persistence.NamedQueries", // NOI18N
752
Collections.singletonList(genUtils.createAnnotationArgument(null, namedQueryAnnotations))));
755
protected void generateRelationship(RelationshipRole role) throws IOException {
756
String memberName = role.getFieldName();
758
// XXX getRelationshipFieldType() does not work well when entity classes
759
// are not all generated to the same package
760
String typeName = getRelationshipFieldType(role, entityClass.getPackage());
761
TypeMirror fieldType = copy.getElements().getTypeElement(typeName).asType();
762
if (role.isToMany()) {
763
// XXX this will probably not resolve imports
764
TypeElement collectionType = copy.getElements().getTypeElement("java.util.Collection"); // NOI18N
765
fieldType = copy.getTypes().getDeclaredType(collectionType, fieldType);
768
List<AnnotationTree> annotations = new ArrayList<AnnotationTree>();
769
List<ExpressionTree> annArguments = new ArrayList<ExpressionTree>();
770
if (role.isCascade()) {
771
annArguments.add(genUtils.createAnnotationArgument("cascade", "javax.persistence.CascadeType", "ALL")); // NOI18N
773
if (role.equals(role.getParent().getRoleB())) {
774
annArguments.add(genUtils.createAnnotationArgument("mappedBy", role.getParent().getRoleA().getFieldName())); // NOI18N
776
if (role.isMany() && role.isToMany()) {
777
List<ExpressionTree> joinTableAnnArguments = new ArrayList<ExpressionTree>();
778
joinTableAnnArguments.add(genUtils.createAnnotationArgument("name", (String) dbMappings.getJoinTableMapping().get(role.getFieldName()))); //NOI18N
780
CMPMappingModel.JoinTableColumnMapping joinColumnMap = dbMappings.getJoinTableColumnMppings().get(role.getFieldName());
782
List<AnnotationTree> joinCols = new ArrayList<AnnotationTree>();
783
String[] colNames = joinColumnMap.getColumns();
784
String[] refColNames = joinColumnMap.getReferencedColumns();
785
for(int colIndex = 0; colIndex < colNames.length; colIndex++) {
786
List<ExpressionTree> attrs = new ArrayList<ExpressionTree>();
787
attrs.add(genUtils.createAnnotationArgument("name", colNames[colIndex])); //NOI18N
788
attrs.add(genUtils.createAnnotationArgument("referencedColumnName", refColNames[colIndex])); //NOI18N
789
joinCols.add(genUtils.createAnnotation("javax.persistence.JoinColumn", attrs)); //NOI18N
791
joinTableAnnArguments.add(genUtils.createAnnotationArgument("joinColumns", joinCols)); // NOI18N
793
List<AnnotationTree> inverseCols = new ArrayList<AnnotationTree>();
794
String[] invColNames = joinColumnMap.getInverseColumns();
795
String[] refInvColNames = joinColumnMap.getReferencedInverseColumns();
796
for(int colIndex = 0; colIndex < invColNames.length; colIndex++) {
797
List<ExpressionTree> attrs = new ArrayList<ExpressionTree>();
798
attrs.add(genUtils.createAnnotationArgument("name", invColNames[colIndex])); //NOI18N
799
attrs.add(genUtils.createAnnotationArgument("referencedColumnName", refInvColNames[colIndex])); //NOI18N
800
inverseCols.add(genUtils.createAnnotation("javax.persistence.JoinColumn", attrs)); // NOI18N
802
joinTableAnnArguments.add(genUtils.createAnnotationArgument("inverseJoinColumns", inverseCols)); // NOI18N
804
annotations.add(genUtils.createAnnotation("javax.persistence.JoinTable", joinTableAnnArguments)); // NOI18N
806
String[] colNames = (String[]) dbMappings.getCmrFieldMapping().get(role.getFieldName());
807
CMPMappingModel relatedMappings = beanMap.get(role.getParent().getRoleB().getEntityName()).getCMPMapping();
808
String[] invColNames = (String[]) relatedMappings.getCmrFieldMapping().get(role.getParent().getRoleB().getFieldName());
809
if (colNames.length == 1) {
810
List<ExpressionTree> attrs = new ArrayList<ExpressionTree>();
811
attrs.add(genUtils.createAnnotationArgument("name", colNames[0])); //NOI18N
812
attrs.add(genUtils.createAnnotationArgument("referencedColumnName", invColNames[0])); //NOI18N
813
makeReadOnlyIfNecessary(pkColumnNames, colNames[0], attrs);
814
annotations.add(genUtils.createAnnotation("javax.persistence.JoinColumn", attrs)); //NOI18N
816
List<AnnotationTree> joinCols = new ArrayList<AnnotationTree>();
817
for(int colIndex = 0; colIndex < colNames.length; colIndex++) {
818
List<ExpressionTree> attrs = new ArrayList<ExpressionTree>();
819
attrs.add(genUtils.createAnnotationArgument("name", colNames[colIndex])); //NOI18N
820
attrs.add(genUtils.createAnnotationArgument("referencedColumnName", invColNames[colIndex])); //NOI18N
821
makeReadOnlyIfNecessary(pkColumnNames, colNames[colIndex], attrs);
822
joinCols.add(genUtils.createAnnotation("javax.persistence.JoinColumn", attrs)); // NOI18N
824
ExpressionTree joinColumnsNameAttrValue = genUtils.createAnnotationArgument(null, joinCols);
825
AnnotationTree joinColumnsAnnotation = genUtils.createAnnotation("javax.persistence.JoinColumns", Collections.singletonList(joinColumnsNameAttrValue)); //NOI18N
826
annotations.add(joinColumnsAnnotation);
831
if (role.isMany() && role.isToMany()) {
832
relationAnn = "ManyToMany"; //NOI18N
833
} else if (role.isMany()) {
834
relationAnn = "ManyToOne"; //NOI18N
835
} else if (role.isToMany()) {
836
relationAnn = "OneToMany"; //NOI18N
838
relationAnn = "OneToOne"; //NOI18N
840
annotations.add(genUtils.createAnnotation("javax.persistence." + relationAnn, annArguments)); // NOI18N
842
properties.add(new Property(Modifier.PRIVATE, annotations, fieldType, memberName));
846
* Creates the <code>serialVersionUID</code> field with
847
* the initial value of <code>1L</code>.
849
* @return the created field.
851
private VariableTree createSerialVersionUID(){
852
Set<Modifier> serialVersionUIDModifiers = new HashSet<Modifier>();
853
serialVersionUIDModifiers.add(Modifier.PRIVATE);
854
serialVersionUIDModifiers.add(Modifier.STATIC);
855
serialVersionUIDModifiers.add(Modifier.FINAL);
857
TreeMaker make = copy.getTreeMaker();
858
VariableTree serialVersionUID = make.Variable(make.Modifiers(serialVersionUIDModifiers),
859
"serialVersionUID", genUtils.createType("long"), make.Literal(Long.valueOf("1"))); //NO18N
861
return serialVersionUID;
864
protected void finish() {
865
// create a constructor which takes the primary key field as argument
866
VariableTree pkFieldParam = genUtils.removeModifiers(pkProperty.getField());
867
List<VariableTree> pkFieldParams = Collections.singletonList(pkFieldParam);
868
constructors.add(genUtils.createAssignmentConstructor(genUtils.createModifiers(Modifier.PUBLIC), entityClassName, pkFieldParams));
870
// if different than pk fields constructor, add constructor
871
// which takes all non-nullable non-relationship fields as args
872
if (nonNullableProps.size() > 0) {
873
List<VariableTree> nonNullableParams = new ArrayList<VariableTree>(nonNullableProps.size() + 1);
874
nonNullableParams.add(pkFieldParam);
875
for (Property property : nonNullableProps) {
876
nonNullableParams.add(genUtils.removeModifiers(property.getField()));
878
constructors.add(genUtils.createAssignmentConstructor(genUtils.createModifiers(Modifier.PUBLIC), entityClassName, nonNullableParams));
881
// create a constructor which takes the fields of the primary key class as arguments
882
if (pkClassVariables.size() > 0) {
883
StringBuilder body = new StringBuilder(30 + 30 * pkClassVariables.size());
884
body.append("{"); // NOI18N
885
body.append("this." + pkProperty.getField().getName() + " = new " + pkClassName + "("); // NOI18N
886
for (Iterator<VariableTree> i = pkClassVariables.iterator(); i.hasNext();) {
887
body.append(i.next().getName());
888
body.append(i.hasNext() ? ", " : ");"); // NOI18N
890
body.append("}"); // NOI18N
891
TreeMaker make = copy.getTreeMaker();
892
constructors.add(make.Constructor(
893
make.Modifiers(EnumSet.of(Modifier.PUBLIC), Collections.<AnnotationTree>emptyList()),
894
Collections.<TypeParameterTree>emptyList(),
896
Collections.<ExpressionTree>emptyList(),
900
// add equals and hashCode methods
901
EntityMethodGenerator methodGenerator = new EntityMethodGenerator(copy, genUtils);
902
methods.add(methodGenerator.createHashCodeMethod(pkFieldParams));
903
methods.add(methodGenerator.createEqualsMethod(entityClassName, pkFieldParams));
904
methods.add(methodGenerator.createToStringMethod(entityFQClassName, pkFieldParams));
906
// add the serialVersionUID field
907
fields.add(createSerialVersionUID());
910
private String getRelationshipFieldType(RelationshipRole role, String pkg) {
911
RelationshipRole rA = role.getParent().getRoleA();
912
RelationshipRole rB = role.getParent().getRoleB();
913
RelationshipRole otherRole = role.equals(rA) ? rB : rA;
914
return pkg.length() == 0 ? otherRole.getEntityName() : pkg + "." + otherRole.getEntityName(); // NOI18N
917
private void makeReadOnlyIfNecessary(List<String> pkColumnNames, String testColumnName, List<ExpressionTree> attrs) {
918
// if the join column is a pk column, add insertable = false, updatable = false
919
if (pkColumnNames.contains(testColumnName)) {
920
attrs.add(genUtils.createAnnotationArgument("insertable", false)); //NOI18N
921
attrs.add(genUtils.createAnnotationArgument("updatable", false)); //NOI18N
927
* An implementation of ClassGenerator which generates primary key
930
private final class PKClassGenerator extends ClassGenerator {
932
public PKClassGenerator(WorkingCopy copy, EntityClass entityClass) throws IOException {
933
super(copy, entityClass);
936
protected void initialize() throws IOException {
937
classTree = genUtils.ensureNoArgConstructor(classTree);
938
// primary key class must be serializable and @Embeddable
939
classTree = genUtils.addImplementsClause(classTree, "java.io.Serializable"); //NOI18N
940
classTree = genUtils.addAnnotation(classTree, genUtils.createAnnotation("javax.persistence.Embeddable")); // NOI18N
943
protected void generateMember(EntityMember m) throws IOException {
944
if (!m.isPrimaryKey()) {
947
Property property = createProperty(m);
948
properties.add(property);
951
protected void afterMembersGenerated() {
954
protected void generateRelationship(RelationshipRole relationship) {
957
protected void finish() {
958
// add a constructor which takes the fields of the primary key class as arguments
959
List<VariableTree> parameters = new ArrayList<VariableTree>(properties.size());
960
for (Property property : properties) {
961
parameters.add(genUtils.removeModifiers(property.getField()));
963
constructors.add(genUtils.createAssignmentConstructor(genUtils.createModifiers(Modifier.PUBLIC), pkClassName, parameters));
965
// add equals and hashCode methods
966
EntityMethodGenerator methodGenerator = new EntityMethodGenerator(copy, genUtils);
967
methods.add(methodGenerator.createHashCodeMethod(parameters));
968
methods.add(methodGenerator.createEqualsMethod(pkClassName, parameters));
969
methods.add(methodGenerator.createToStringMethod(pkFQClassName, parameters));