I'm trying to use a base class for my mongo collections and then have the collection name come from the derived classes; however, the collection name is always just entity ( instead of, in this case, Derived).
I have the following abstract class:
public abstract class Entity {
#Id
private String id;
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
}
And derived classes like:
// I've also tried #TypeAlias("Derived")
#Document(collection = "Derived")
public class Derived extends Entity {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
With a repository like:
#Component
public interface Repository<T extends Entity> extends MongoRepository<T, String> {
T findById(String id);
}
Follow this working example.
Let's declare a base document model from which every document model must inherit
import java.math.BigInteger;
import org.springframework.data.annotation.Id;
public class BaseDocument {
#Id
private BigInteger id;
public BigInteger getId() {
return id;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (this.id == null || obj == null || !(this.getClass().equals(obj.getClass()))) {
return false;
}
BaseDocument that = (BaseDocument) obj;
return this.id.equals(that.getId());
}
#Override
public int hashCode() {
return id == null ? 0 : id.hashCode();
}
}
Just for example, I declare my Code model. Note that collection name, if differs from class name, can be set with #Document annotation
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
#Document(collection = "codes")
public class Code extends BaseDocument {
#Field("code")
private String code;
#Field("holiday")
private String holiday;
public Code(String code, String holiday) {
super();
this.code = code;
this.holiday = holiday;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getHoliday() {
return holiday;
}
public void setHoliday(String holiday) {
this.holiday = holiday;
}
#Override
public String toString() {
return "Code [code=" + code + ", holiday=" + holiday + "]";
}
}
At this point you can easily use these classes using MongoOperations and other classes in order to operate on database and collections. MongoOperations has all the methods you need to operate on collections such as: findOne, findAll and so on.
Just to be clear, you can use MongoOperations in the following way:
MongoOperations mongoOps = new MongoTemplate(new SimpleMongoDbFactory(new MongoClient(), "yourDBname"));
I strongly suggest to implement a service class which operate on collection, like this one, instead of operating directly on it:
import java.util.List;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
public class CodesService extends BaseService implements BaseOperations{
#Override
public BaseDocument findOne(Long id) {
Query query = new Query(Criteria.where("code").is("8"));
return this.mongoOps.findOne(query, Code.class);
}
#Override
public List<? extends BaseDocument> findAll() {
List<Code> subs = this.mongoOps.findAll(Code.class);
List<? extends BaseDocument> codes = subs;
return codes;
}
}
This class implements the following interface:
import java.util.List;
public interface BaseOperations {
public BaseDocument findOne(Long id);
public List<? extends BaseDocument> findAll();
}
Hope this may help.
Try to implement the Repository<T extends Entity> interface like given below
public interface SomeInterfaceName extends Repository<Derived>{
}
Now try to #Autowire the Repository<Derived> repo
It will give you the desired output
Related
I'm writing an app using Spring Boot, Hiberane and Spring Data.
I have two tables in the db: tableA and tableB.
They have some common fields but their id's,name's are different, also I've created a basic model for them to contain some common fields, right now it looks something like this:
// BaseModel
#MappedSuperclass
public abstract class BaseModel implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name="common_field_1")
private String commonField1;
#Column(name="common_field_2")
private String commonField2;
#Column(name="common_field_3")
private String commonField3;
}
// ExactModel 1
#Entity
#Table(name="table1" ,schema="schema")
public class ExactModel1 extends BaseModel {
#Id
#Basic(fetch=FetchType.EAGER)
#Column(name="exact_model_id_1", nullable=false)
private long exactModel1Id;
private String exactField1;
}
// ExactModel 2
#Entity
#Table(name="table2" ,schema="schema")
public class ExactModel2 extends BaseModel {
#Id
#Basic(fetch=FetchType.EAGER)
#Column(name="exact_model_id_2", nullable=false)
private long exactModel2Id;
private String exactField2;
}
And I have some generic logic which implements some general crud logic which works for classes which extend BaseModel:
public abstract class BaseServiceImpl<M extends BaseModel, R extends BaseRepository<M>> implements BaseService<M, Long> {
private final R repository;
public BaseServiceImpl(R repository) {
this.repository = repository;
}
#Override
public M save(M model) {
return repository.save(model);
}
#Override
public List<M> saveAll(List<M> models) {
return repository.saveAll(models);
}
#Override
public M findById(Long id) {
return repository.getOne(id);
}
#Override
public List<M> findAllById(List<Long> ids) {
return repository.findAllById(ids);
}
#Override
public List<M> findAll() {
return repository.findAll();
}
#Override
public M update(M model) {
return repository.save(model);
}
#Override
public List<M> updateAll(List<M> models) {
return repository.saveAll(models);
}
#Override
public void delete(M model) {
repository.delete(model);
}
#Override
public void delteById(Long id) {
repository.deleteById(id);
}
#Override
public void deleteInBatch(List<M> models) {
repository.deleteInBatch(models);
}
#Override
public Long countModels() {
return repository.count();
}
}
The thing is now I need to get somehow the id of the entity I work with in this generic logic, but there is no id field in BaseModel, so I can't just use baseModel.getId().
The question: is it possible to define a mock id field in BaseModel and override it in the child classes, so I can use this id in the generic methods but Hibernate fills the actual ids on the runtime for me?
I have a spring boot (1.5.4.RELEASE) project using Java 8. I have an entity and it's related domain class like this:
#Entity
#Table(name = "Foo", schema = "dbo")
public class FooEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private int id;
#Column(name="Name")
private String name;
#Column(name="Type")
private String type;
#Column(name="Color")
private String color;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "Car")
private Car car;
//getter and setter
}
public class Foo {
private int id;
private String name;
private String type;
private String color;
private Car car;
//Constructors and getters
}
I want to create a repository that fetches this Foo object from the DB but only fetching the complex fields if the user asks for them to prevent unnecessary join statements. The repo looks like this:
import static com.test.entities.QFooEntity.fooEntity;
import static com.test.entities.QCarEntity.carEntity;
#Repository
public class FooRepository {
private final JPAQuery<FooEntity> query = createQuery().from(fooEntity);
public FooRepository getFooByName(String name) {
query.where(fooEntity.name.eq(name));
return this;
}
public FooRepository withCar() {
query.leftJoin(fooEntity.car, carEntity).fetchJoin();
return this;
}
public Foo fetch() {
FooEntity entity = query.fetchOne();
return FooMapper.mapEntityToDomain().apply(entity);
}
}
So a barebones call for a Foo object will return the Entity with values for all the fields except for the car field. If the user wants car information then they have to explicitly call withCar.
Here is the mapper:
public class FooMapper {
public static Function<FooEntity, Foo> mapEntityToDomain() {
return entity -> {
return new Foo(e.getId(), e.getName(), e.getType(), e.getColor(), e.getCar());
};
}
}
The problem is when you do e.getCar() if the value is not there (i.e. there's a proxy present) JPA will go out and fetch it for you. I don't want this to be the case. It will just grab the values and map them to the domain equivalent if it's not there then null.
One solution that I've heard (and tried) is calling em.detach(entity); however, this doesn't work as I intended because it throws an exception when you try to access getCar and I've also heard this is not best practice.
So my question is what is the best way to create a repo using a builder pattern on a JPA entity and not have it call the DB when trying to map.
You could create a utility method that will return null if the given object is a proxy and is not initialized:
public static <T> T nullIfNotInitialized(T entity) {
return Hibernate.isInitialized(entity) ? entity : null;
}
Then you can call the method wherever you need it:
return new Foo(e.getId(), e.getName(), e.getType(), e.getColor(), nullIfNotInitialized(e.getCar()));
Just map it to a new object and leave out the Car relation, this is the standard approach. You can use MapStruct and just ignore the car field during mapping: http://mapstruct.org/documentation/stable/reference/html/#inverse-mappings
Just don't map the car... Map a field holding the ID and use another method to get the actual Car. I would use a distinctive method name, to differentiate it from the other getters.
class FooEntity {
#Column
private int carId;
public int getCarId() {
return carId;
}
public void setCarId(int id) {
this.carId = id;
}
public Car fetchCar(CarRepository repo) {
return repo.findById(carId);
}
}
You can write query on top of JPA
#Query("select u from Car c")
import org.springframework.data.repository.CrudRepository;
import com.example.model.FluentEntity;
public interface DatabaseEntityRepository extends CrudRepository<FooEntity , int > {
}
As you said
I don't want this to be the case. It will just grab the values and map them to the domain equivalent, if it's not there then null.
Then you just set it to null, because the field car will always not be there.
Otherwise, if you mean not there is that the car not exists in db, for sure a subquery(call the proxy) should be made.
If you want to grab the car when call Foo.getCar().
class Car {
}
class FooEntity {
private Car car;//when call getCar() it will call the proxy.
public Car getCar() {
return car;
}
}
class Foo {
private java.util.function.Supplier<Car> carSupplier;
public void setCar(java.util.function.Supplier<Car> carSupplier) {
this.carSupplier = carSupplier;
}
public Car getCar() {
return carSupplier.get();
}
}
class FooMapper {
public static Function<FooEntity, Foo> mapEntityToDomain() {
return (FooEntity e) -> {
Foo foo = new Foo();
foo.setCar(e::getCar);
return foo;
};
}
}
Make sure you have the db session ,when you call Foo.getCar()
You could try adding state to your repository and influence the mapper. Something like this:
import static com.test.entities.QFooEntity.fooEntity;
import static com.test.entities.QCarEntity.carEntity;
#Repository
public class FooRepository {
private final JPAQuery<FooEntity> query = createQuery().from(fooEntity);
private boolean withCar = false;
public FooRepository getFooByName(String name) {
query.where(fooEntity.name.eq(name));
return this;
}
public FooRepository withCar() {
query.leftJoin(fooEntity.car, carEntity).fetchJoin();
withCar = true;
return this;
}
public Foo fetch() {
FooEntity entity = query.fetchOne();
return FooMapper.mapEntityToDomain(withCar).apply(entity);
}
}
In your mapper, you then include a switch to enable or disable car lookups:
public class FooMapper {
public static Function<FooEntity, Foo> mapEntityToDomain(boolean withCar) {
return e -> {
return new Foo(e.getId(), e.getName(), e.getType(), e.getColor(), withCar ? e.getCar() : null);
};
}
}
If you then use new FooRepository().getFooByName("example").fetch() without the withCar() call, e.getCar() should not be evaluated inside FooMapper
You may want to use the PersistentUnitUtil class to query if an attribute of entity object is already loaded or not. Based on that you may skip the call to corresponding getter as shown below. JpaContext you need to supply to user entity bean mapper.
public class FooMapper {
public Function<FooEntity, Foo> mapEntityToDomain(JpaContext context) {
PersistenceUnitUtil putil = obtainPersistentUtilFor(context, FooEntity.class);
return e -> {
return new Foo(
e.getId(),
e.getName(),
e.getType(),
e.getColor(),
putil.isLoaded(e, "car") ? e.getCar() : null);
};
}
private PersistenceUnitUtil obtainPersistentUtilFor(JpaContext context, Class<?> entity) {
return context.getEntityManagerByManagedType(entity)
.getEntityManagerFactory()
.getPersistenceUnitUtil();
}
}
My question is actually a spin-off of this question as seen here... so it might help to check that thread before proceeding.
In my Spring Boot project, I have two entities Sender and Recipient which represent a Customer and pretty much have the same fields, so I make them extend the base class Customer;
Customer base class;
#MappedSuperclass
public class Customer extends AuditableEntity {
#Column(name = "firstname")
private String firstname;
#Transient
private CustomerRole role;
public Customer(CustomerRole role) {
this.role = role;
}
//other fields & corresponding getters and setters
}
Sender domain object;
#Entity
#Table(name = "senders")
public class Sender extends Customer {
public Sender(){
super.setRole(CustomerRole.SENDER);
}
}
Recipient domain object;
#Entity
#Table(name = "recipients")
public class Recipient extends Customer {
public Recipient(){
super.setRole(CustomerRole.RECIPIENT);
}
}
NOTE - Sender and Recipient are exactly alike except for their roles. These can be easily stored in a single customers Table by making the Customer base class an entity itself, but I intentionally separate the entities this way because I have an obligation to persist each customer type in separate database tables.
Now I have one form in a view that collects details of both Sender & Recipient, so for example to collect the firstname, I had to name the form fields differently as follows;
Sender section of the form;
<input type="text" id="senderFirstname" name="senderFirstname" value="$!sender.firstname">
Recipient section of the form;
<input type="text" id="recipientFirstname" name="recipientFirstname" value="$!recipient.firstname">
But the fields available for a customer are so many that I'm looking for a way to map them to a pojo by means of an annotation as asked in this question here. However, the solutions provided there would mean that I have to create separate proxies for both domain objects and annotate the fields accordingly e.g
public class SenderProxy {
#ParamName("senderFirstname")
private String firstname;
#ParamName("senderLastname")
private String lastname;
//...
}
public class RecipientProxy {
#ParamName("recipientFirstname")
private String firstname;
#ParamName("recipientLastname")
private String lastname;
//...
}
So I got very curious and was wondering, is there a way to map this Proxies to more than one #ParamName such that the base class for example can just be annotated as follows?;
#MappedSuperclass
public class Customer extends AuditableEntity {
#Column(name = "firstname")
#ParamNames({"senderFirstname", "recipientFirstname"})
private String firstname;
#Column(name = "lastname")
#ParamNames({"senderLastname", "recipientLastname"})
private String lastname;
#Transient
private CustomerRole role;
public Customer(CustomerRole role) {
this.role = role;
}
//other fields & corresponding getters and setters
}
And then perhaps find a way to select value of fields based on annotation??
A suggestion from Zhang Jie like ExtendedBeanInfo
so i do it this way
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Alias {
String[] value();
}
public class AliasedBeanInfoFactory implements BeanInfoFactory, Ordered {
#Override
public BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
return supports(beanClass) ? new AliasedBeanInfo(Introspector.getBeanInfo(beanClass)) : null;
}
private boolean supports(Class<?> beanClass) {
Class<?> targetClass = beanClass;
do {
Field[] fields = targetClass.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Alias.class)) {
return true;
}
}
targetClass = targetClass.getSuperclass();
} while (targetClass != null && targetClass != Object.class);
return false;
}
#Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 100;
}
}
public class AliasedBeanInfo implements BeanInfo {
private static final Logger LOGGER = LoggerFactory.getLogger(AliasedBeanInfo.class);
private final BeanInfo delegate;
private final Set<PropertyDescriptor> propertyDescriptors = new TreeSet<>(new PropertyDescriptorComparator());
AliasedBeanInfo(BeanInfo delegate) {
this.delegate = delegate;
this.propertyDescriptors.addAll(Arrays.asList(delegate.getPropertyDescriptors()));
Class<?> beanClass = delegate.getBeanDescriptor().getBeanClass();
for (Field field : findAliasedFields(beanClass)) {
Optional<PropertyDescriptor> optional = findExistingPropertyDescriptor(field.getName(), field.getType());
if (!optional.isPresent()) {
LOGGER.warn("there is no PropertyDescriptor for field[{}]", field);
continue;
}
Alias alias = field.getAnnotation(Alias.class);
addAliasPropertyDescriptor(alias.value(), optional.get());
}
}
private List<Field> findAliasedFields(Class<?> beanClass) {
List<Field> fields = new ArrayList<>();
ReflectionUtils.doWithFields(beanClass,
fields::add,
field -> field.isAnnotationPresent(Alias.class));
return fields;
}
private Optional<PropertyDescriptor> findExistingPropertyDescriptor(String propertyName, Class<?> propertyType) {
return propertyDescriptors
.stream()
.filter(pd -> pd.getName().equals(propertyName) && pd.getPropertyType().equals(propertyType))
.findAny();
}
private void addAliasPropertyDescriptor(String[] values, PropertyDescriptor propertyDescriptor) {
for (String value : values) {
if (!value.isEmpty()) {
try {
this.propertyDescriptors.add(new PropertyDescriptor(
value, propertyDescriptor.getReadMethod(), propertyDescriptor.getWriteMethod()));
} catch (IntrospectionException e) {
LOGGER.error("add field[{}] alias[{}] property descriptor error", propertyDescriptor.getName(),
value, e);
}
}
}
}
#Override
public BeanDescriptor getBeanDescriptor() {
return this.delegate.getBeanDescriptor();
}
#Override
public EventSetDescriptor[] getEventSetDescriptors() {
return this.delegate.getEventSetDescriptors();
}
#Override
public int getDefaultEventIndex() {
return this.delegate.getDefaultEventIndex();
}
#Override
public PropertyDescriptor[] getPropertyDescriptors() {
return this.propertyDescriptors.toArray(new PropertyDescriptor[0]);
}
#Override
public int getDefaultPropertyIndex() {
return this.delegate.getDefaultPropertyIndex();
}
#Override
public MethodDescriptor[] getMethodDescriptors() {
return this.delegate.getMethodDescriptors();
}
#Override
public BeanInfo[] getAdditionalBeanInfo() {
return this.delegate.getAdditionalBeanInfo();
}
#Override
public Image getIcon(int iconKind) {
return this.delegate.getIcon(iconKind);
}
static class PropertyDescriptorComparator implements Comparator<PropertyDescriptor> {
#Override
public int compare(PropertyDescriptor desc1, PropertyDescriptor desc2) {
String left = desc1.getName();
String right = desc2.getName();
for (int i = 0; i < left.length(); i++) {
if (right.length() == i) {
return 1;
}
int result = left.getBytes()[i] - right.getBytes()[i];
if (result != 0) {
return result;
}
}
return left.length() - right.length();
}
}
}
Ok so I am new to spring and don't really know how this works. I have been trying a few things and think its close to doing it but not getting any data from the server and giving me this error
Unsatisfied dependency expressed through constructor argument with index 4 of type [jp.co.fusionsystems.dimare.crm.service.impl.MyDataDefaultService]: : Error creating bean with name 'MyDataDefaultService' defined in file
My end point
//mobile data endpoint
#RequestMapping(
value = API_PREFIX + ENDPOINT_MyData + "/getMyData",
method = RequestMethod.GET)
public MyData getMyData() {
return MyDataDefaultService.getData();
}
My Object
public class MyData {
public MyData(final Builder builder) {
videoLink = builder.videoLink;
}
private String videoLink;
public String getVideoLink()
{
return videoLink;
}
public static class Builder
{
private String videoLink = "";
public Builder setVideo(String videoLink)
{
this.videoLink = videoLink;
return this;
}
public MyData build()
{
return new MyData(this);
}
}
#Override
public boolean equals(final Object other) {
return ObjectUtils.equals(this, other);
}
#Override
public int hashCode() {
return ObjectUtils.hashCode(this);
}
#Override
public String toString() {
return ObjectUtils.toString(this);
}
}
The Repository
public classMyServerMyDataRepository implements MyDataRepository{
private finalMyServerMyDataJpaRepository jpaRepository;
private final MyDataConverter MyDataConverter = new MyDataConverter();
#Autowired
publicMyServerMyDataRepository(finalMyServerMyDataJpaRepository jpaRepository) {
this.jpaRepository = Validate.notNull(jpaRepository);
}
#Override
public MyData getData() {
MyDataEntity entity = jpaRepository.findOne((long) 0);
MyData.Builder builder = new MyData.Builder()
.setVideo(entity.getVideoLink());
return builder.build();
}
The DefaultService that gets called by the endpoint
public class MyDataDefaultService {
private static final Logger logger = LoggerFactory.getLogger(NotificationDefaultService.class);
private finalMyServerMyDataRepository repository;
#Autowired
public MyDataDefaultService(MyServerMyDataRepository repository) {
this.repository = Validate.notNull(repository);
}
//Get the data from the server
public MobileData getData()
{
logger.info("Get Mobile Data from the server");
//Get the data from the repository
MobileData mobileData = repository.getData();
return mobileData;
}
}
The Converter
public class MyDataConverter extends AbstractConverter<MyDataEntity, MyData>
{
#Override
public MyData convert(MyDataEntity entity) {
MyData.Builder builder = new MyData.Builder()
.setVideo(entity.getVideoLink());
return builder.build();
}
}
My Entity
#Entity
#Table(name = “myServer”)
public class MyDataEntity extends AbstractEntity{
#Column(name = "video_link", nullable = true)
private String videoLink;
public String getVideoLink() {
return videoLink;
}
public void setVideoLink(final String videoLink) {
this.videoLink = videoLink;
}
}
Thank you for any help with this
Hibernate entity should have default constructor defined and implement Serializable interface as well, assume AbstractEntity matches the requirement. Hibernate won't accept an entity without a primary key so you have to define the one too:
#Entity
#Table(name = “myServer”)
public class MyDataEntity implements Serializable {
#Id
#GeneratedValue
private Long id;
#Column(name = "video_link", nullable = true)
private String videoLink;
public MyDataEntity() {
}
...setters&getters
}
MyData object represents the JSON server response, you can use Jackson annotations to control the result JSON properties:
public class MyDataResponse {
#JsonProperty("video_link")
private String videoLink;
public MyDataResponse() {
}
public MyDataResponse(String videoLink) {
this.videoLink = videoLink;
}
...setters&getters
}
Spring has an awesome project so called Spring Data that provides the JPA repositories, so there's no even the #Repository annotation ever needed:
public class MyDataRepository extends CrudRepository<MyDataEntity, Long> {
}
The Builder class represents the Service layer:
#Service
public class MyDataService {
#Autowired
private MyDataRepository myDataRepository;
public MyDataResponse getMyData(Long id) {
MyDataEntity entity = myDataRepository.findOne(id);
...rest logic, copy necessary data to MyDataResponse
}
}
Then a controller is:
#RestController // #ResponseBody not needed when using like this
public MyDataController {
#Autowired
private MyDataService myDataService;
#RequestMapping("/getMyData") // no need to specify method for GET
public MyDataResponse getMyData(#RequestParam("ID") Long myDataId) {
... validation logic
return myDataService.getMyData(myDataId); // return response
}
}
Now it should work, don't forget to add required dependencies to your classpath.
First of all, sorry for my english.
Im making, for a friend, a desktop application with JPA(EclipseLink) that access a SQLite database.
I already created the database and the entities in Eclipse. But i also created a class called UniversalDAO which is an utility class used by all the entities to access and persist the database:
package model.DAO;
import java.util.ArrayList;
import javax.persistence.*;
import model.entities.Entities;
public class UniversalDAO {
private static EntityManagerFactory emf = Persistence.createEntityManagerFactory("TheDatabase");
private static EntityManager em = emf.createEntityManager();
private UniversalDAO (){}
public static void close(){
em.close();
emf.close();
}
public static Entities getOne(Class<? extends Entities> table, Object primaryKey) {
return em.find(table, primaryKey);
}
public static ArrayList<Entities> getAll(Class<? extends Entities> table) {
ArrayList<Entities> ret = new ArrayList<>();
for(Object obj : em.createQuery("SELECT o FROM " + table.getName() + " o").getResultList())
ret.add((Entities) obj);
return ret;
}
public static ArrayList<Entities> getWithCondition(Class<? extends Entities> table, String condition) {
ArrayList<Entities> ret = new ArrayList<>();
for(Object obj : em.createQuery("SELECT o FROM " + table.getName() + " o WHERE " + condition).getResultList())
ret.add((Entities) obj);
return ret;
}
public static void insert(Entities row) {
em.getTransaction().begin();
em.persist(row);
em.flush();
em.getTransaction().commit();
}
public static void update(Entities row) {
em.getTransaction().begin();
em.merge(row);
em.flush();
em.getTransaction().commit();
}
public static void delete(Class<? extends Entities> table, Object primaryKey) {
em.getTransaction().begin();
Entities row = em.find(table, primaryKey);
em.remove(row);
em.flush();
em.getTransaction().commit();
}
}
To group all the entites and use them in this class i created an empty interface called Entities.
This is how one of the entities looks like:
package model.entities;
import java.util.ArrayList;
import javax.persistence.*;
#Entity
#Table(name="emails")
public class EntityEmail implements Entities {
#Id
#Column(name="id_email")
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private int idEmail;
#Column(name="email")
private String email;
#Column(name="description")
private String description;
#ManyToMany(fetch=FetchType.EAGER)
#JoinTable(name="people_emails",
joinColumns=#JoinColumn(name="id_email", referencedColumnName="id_email"),
inverseJoinColumns=#JoinColumn(name="id_person", referencedColumnName="id_person"))
private ArrayList<EntityPerson> people;
public EntityEmail() {
}
public int getIdEmail() {
return this.idEmail;
}
public void setIdEmail(int idEmail) {
this.idEmail = idEmail;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public ArrayList<EntityPerson> getPeople() {
return people;
}
public void setPeople(ArrayList<EntityPerson> people) {
this.people = people;
}
}
Like you can appreciate im not a professional and i still have a lot to learn.
So, i was wondering if this approach is correct or if i should have one DAO for each entity.
It looks like you try to invent Generic DAO pattern. If so, you're essentially on the right way.
Generic DAO pattern works as follows:
Create a generic base class that all your DAOs will extend:
public abstract class GenericDao<E, ID extends Serializable> {
...
// Implement common operations that are relevant to all entities here:
public E findById(ID id) { ... }
public void save(E entity) { ... }
// etc
...
}
Create concrete DAO implementations by extending GenericDao:
public class EntityEmailDao extends GenericDao<EntityEmail, Integer> {
// This class may contain operations that are relevant to specific entity:
public E findByEmail(String email) { ... }
}
Since GenericDao is generic, you don't need to make your entities extend any common interface
There are plenty of exisiting implementations of this pattern around, take a look, for example, here.