What I have setup are two tables, one for a user created account, and the other that lets the user buy a product.
I have both tables set up like so
Customer Table
#PrimaryKey(autoGenerate = true)
private int custId;
#ColumnInfo(name = "user_name")
private String userName;
#ColumnInfo(name = "password")
private String password;
#ColumnInfo(name = "first_name")
private String firstName;
#ColumnInfo(name = "last_name")
private String lastName;
#ColumnInfo(name = "address")
private String address;
#ColumnInfo(name = "city")
private String city;
#ColumnInfo(name = "postal_code")
private String postalCode;
#ColumnInfo(name = "country")
private String country;
Phone Table
#PrimaryKey(autoGenerate = true)
private int productId;
private String phoneMake;
private String phoneModel;
private String phoneColor;
private String storageCapacity;
private Float price;
What I have set up are two foreign keys, one in each table. My last table is for ordering the phones, which requires using both Primary Keys from each table. What I feel like I need is a ForeignKey, similar in vein to the PrimaryKey already created. The problem is that I am unsure how to implement that into the program. Everything I try doing is not working. I have looked at the documentation, but nothing clicks. I hope you can help me with the correct screenshot. If more is needed let me know (This code is written in Java code)
If you simply want a Customer to have 1 phone, then you have have a single column (member variable) for the relationship that will store the phone's product id.
e.g.
private int mapToPhone; //<<<<< ADDED no need for #ColumnInfo the column name will be as per the variable name.
Obviously you set the value to an appropriate value.
To then get the Customer with the phone's details then you have a POJO that embeds the parent (Customer) using the #Embedded annotation has the child (Phone) using the #Relation annotation.
e.g. :-
class CustomerWithPhoneDetails {
#Embedded
Customer customer;
#Relation(
entity = Phone.class,
parentColumn = "mapToPhone",
entityColumn = "productId"
)
Phone phoneDetails;
}
You can then have a method in the #Dao annotated interface/abstract class which queries the parent table BUT returns the POJO or list/array of the POJO e.g. :-
#Query("SELECT * FROM Customer")
abstract List<CustomerWithPhoneDetails> getAllCustomersWithPhoneDeytails();
Example
Based upon your code, and the additional example code along with an #Database annotated abstract class :-
#Database(entities = {Customer.class,Phone.class}, version = 1, exportSchema = false)
abstract class TheDatabase extends RoomDatabase {
abstract AllDao getAllDao();
private static volatile TheDatabase instance = null;
public static TheDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context,TheDatabase.class,"the_database.db")
.allowMainThreadQueries()
.build();
}
return instance;
}
}
and an Activity e.g. :-
public class MainActivity extends AppCompatActivity {
TheDatabase db;
AllDao dao;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = TheDatabase.getInstance(this);
dao = db.getAllDao();
long phone01ProductId = dao.insert(new Phone("PhoneMaker001","Model001","Color001","100Mb",111.11F));
long phone02ProductId = dao.insert(new Phone("PhoneMaker002","Model002","Color002","200Mb",222.22F));
dao.insert(new Customer("c001","password001","firstname001","lastname001","address001","city001","country001","postcode001",(int) phone01ProductId));
dao.insert(new Customer("c002","password002","firstname002","lastname002","address002","city002","country002","postcode002",(int) phone02ProductId));
for(CustomerWithPhoneDetails cwpd: dao.getAllCustomersWithPhoneDeytails()) {
Log.d("DBINFO","Customer is " + cwpd.customer.getUserName() + " etc. Phone is " + cwpd.phoneDetails.getProductId() + " etc." );
}
}
}
Note that suitable constructors have been coded in both the Phone and Customer class (default/empty constructor and one, annotated with #Ignore annotation that allows all values bar the id to be passed as used in the example below)
Note that ideally long rather than int should be used for the id columns.
Results
The Log :-
D/DBINFO: Customer is c001 etc. Phone is 1 etc.
D/DBINFO: Customer is c002 etc. Phone is 2 etc.
App Inspection :-
and :-
Related
I use h2 in memory db and I don't want to create duplicate locations in my DataBase. Only when I use createItem and input location column id manualy it write it to the same location. Otherwise even if the country city gps coordinates are the same app write it to other location with it's id.
I tried to understand but It's not working
I got these entities.
#Entity
#Table(name = "item_System_items")
public class Item {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
private String description;
private BigDecimal price;
private Integer stock;
#ManyToOne
#JoinColumn(name = "location_id")
#Cascade(value={org.hibernate.annotations.CascadeType.ALL})
private Location location;
And
#Entity
#Table(name = "item_System_locations")
public class Location {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String country;
private String city;
private String street;
private String gpsCoordinates;
SETTERS AND GETTERS IS THERE I JUST NOT POST THEM HERE
Controller
#RestController
#RequestMapping("/items")
public class ItemsController {
#Autowired
private ItemsService service;
#PostMapping
#ResponseStatus(value=HttpStatus.CREATED)
public int createItem(#RequestBody Item item) {
return service.createItem(item);
}
Service
#Service
#Transactional
public class ItemsService {
#Autowired
private ItemJPARepository repository;
public int createItem(Item item) {
return repository.save(item).getId();
}
I expect after re-coding app doesn't make new location if the column values are the same.
Thank you people!
If you really help me I would be so happy!
There is nothing in your Entity definitions to tell the ORM about the constraint you want.
You can add #UniqueConstraint to the #Table in your Location entity and specify which column(s) must all be in a unique combination (example given in the linked documentation):
#Table(
name="EMPLOYEE",
uniqueConstraints=
#UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})
)
This will add a check in the database, if the ORM is managing your database schema, which will throw an exception when violated.
I want to pull data from two tables bookings table and routes table. Bookings table contains a foreign key route_id. What I want to do is spool from bookings table but also get the departure time and departure date from the routes table based on the foreign key which is a primary key on the routes table.
I tried the following :
#Entity
public class bookings {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Nullable
private String email;
private String amount;
private String phoneId;
private Timestamp dateCreated;
private int state;
/* Bokkings state will have 3 states 1 => pending, 2 => booked and 3 => used*/
#ManyToOne
private routes routes;
}
The routes entity :
#Entity
public class routes {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne
private busStations busStations;
private String destination;
private String price;
private String departureTime; // So same routes but different depature times
private Date departureDate; // So same routes but different depature dates
private Timestamp dateCreated;
#Nullable
private String dateModified;
}
I put the following in my Crud Repo Interface
public interface bookingsRepository extends CrudRepository<bookings,
Integer> {
List<bookings> findByRoutesDepartureDateAndState(Date date, int state);
}
The issue I have is I can not pull the values from the tables with the Query I wrote in my Crud Interface. I know the query is wrong but I don't know how to fix it.
I think you can use interface with findByStateAndRoutesDepartureDate(int state, Date departureDate) method name...
Anyway I think that use JPA Specification is a better way to implement this kind of query.
I want to get some data from db using custom query.
#Query("select new com.myProject.UserConfDTO(cd.id, us.lastDeactivationTime, true) from UserConfig us " +
"join us.codes as cd where cd in :codes and us.userEnabled = 1 and us.state= 'ACTIVE'")
List<UserConfDTO> getAllEnabledUsersWithConf(#Param("codes") List<Codes> codes);
Codes.java:
#AllArgsConstructor
public class Codes{
private Integer id;
private String name;
}
UserConfDTO.java:
#AllArgsConstructor
public class UserConfDTO{
private Integer id;
private String name;
private Instant lastDeactivationTime;
private Boolean userEnabled;
}
UserConfig.java:
#Id
#GeneratedValue
private Long id;
#ElementCollection
#Builder.Default
private Set<Integer> codes = new HashSet<>();
private Instant lastDeactivationTime;
#Column(nullable = false)
private State state;
I would like to pass all Codes objects - check some things in db, and return prepared UserConfDT object. Unfortunately, It doesn`t work. I get exception:
IllegalArgumentException: Parameter value element [Codes(id=1234, name=test1)] did not match expected type [java.lang.Integer (n/a)]
I have UserConfig class/table with relation one-many with class/table Codes. One UserConfig can have more then one codes.
I want to pass List as parameter and fetch from UserConfig data (by each Codes ID property) --> next create via (select new..) UserConfDTO object.
Do you know how to do it?
I am using hibernate 4. I am writing a filter. The strange thing I noticed is the filter is not getting applied if I use session.get() method
public SecurityAgency getSecurityAgencyById(int id) {
Session session = this.sessionFactory.getCurrentSession();
session.enableFilter("byEnabled");
SecurityAgency s = (SecurityAgency)session.get(SecurityAgency.class, new Integer(id));
return s;
}
Filter starts working as soon as I replace the session.get method with session.createQuery method and send a HQL query. I am unable to find any reason for this behaviour in the hibernate documentation.
FIlter declaration in securtiy agency class
#Entity
#Table(name="security_agency")
public class SecurityAgency implements java.io.Serializable {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name="name")
private String name;
#Column(name="code")
private String code;
#Column(name="website")
private String website;
#Column(name="tan")
private String tan;
#Column(name="email")
private String email;
#Column(name="pan")
private String pan;
#Column(name="created_at")
private Date createdAt;
#Column(name="created_by")
private long createdBy;
#Column(name="modified_at")
private Date modifiedAt;
#Column(name="modified_by")
private long modifiedBy;
#OneToMany(mappedBy="securityAgency",fetch = FetchType.EAGER)
#JsonIgnoreProperties("securityAgency")
#Filter(name = "byEnabled", condition = "is_enabled= 1")
private Set<ContactPerson> contactPersons = new HashSet<ContactPerson>(0);
public SecurityAgency() {
}
Contact person class
#Entity
#Table(name = "contact_person")
#FilterDefs({
#FilterDef(name="byEnabled"),
#FilterDef(name="bySecurityAgency",parameters = #ParamDef(name="agency_id", type="int"))
})
#Filters({
#Filter(name="byEnabled", condition = "is_enabled = 1"),
#Filter(name="bySecurityAgency", condition = "agency_id= :agency_id ")
})
public class ContactPerson implements java.io.Serializable {
Filter doesn't work if you are fetching using id value.Use Query interface instead. See this thread
if you want to use table column values you need to use filter join table ( #FilterJoinTable ), #Filter is applied to target entity rather than table
try,
#FilterJoinTable(name = "byEnabled", condition = "is_enabled= :enabled")
private Set<ContactPerson> contactPersons = new HashSet<ContactPerson>(0);
get
session.enableFilter("byEnabled").setParameter("enabled", Integer.valueOf(1));
Really appreciate ANY help (at least ways how to trace root cause of the problem) because I've been fighting with this for several days and didn't find even workaround.
The problem itself: I have a few entities, all of them work good - persist(), find() etc. except one method where I create two different entities (Order and Items, one order can have many Items). After calling em.persist(..) order is saved and I see its id generated by DB, item is saved to DB (I see it through SELECT directly in DB) but it shows ID=0. And whatever I do it always 0 (e.g. when I open the order I still see its ID=0) until I restart server - then it shows correct ID of item.
Code of the method (after logging I added actual values I get):
public void createOrderFromItems(ArrayList<TehnomirItemDTO> items, User user) {
Order ord = new Order();
User managers = getUserByEmail(Constants.ALL_MANAGERS_GROUP);
ord.setAssignedTo(managers);
Date date = new Date();
ord.setCreatedOn(date);
User customer = user;
ord.setCustomer(customer);
BigDecimal custBalance = new BigDecimal(0);
ArrayList<Balance> balances = getBalanceForUser(customer);
for (Balance b:balances) {
custBalance.add(b.getAmount());
}
logger.debug("before1. order: "+ord.getOrderId()); //here I get 0
em.persist(ord);
logger.debug("before2. order: "+ord.getOrderId()); //still 0
State new_state = getStateByName(SharedConstants.STATE_NEW);
logger.debug("before3. order: "+ord.getOrderId()); //here I get actual ID, generated by DB, e.g. 189
State overpriced = getStateByName(SharedConstants.STATE_LIMIT_EXCEEDED);
ArrayList<Item> itemList = new ArrayList<Item>();
for (TehnomirItemDTO tid:items) {
Item item = new Item(tid);
item.setOrder(ord);
logger.debug("order inside2:"+ord.getOrderId()); //again, actual ID
item.setPriceInt(tid.getPrice_int());
custBalance = custBalance.subtract(item.getPriceInt());
if (custBalance.floatValue()>0) {
item.setStateBean(new_state);
} else item.setStateBean(overpriced);
logger.debug("item before:"+item.getItemId()); //here I get 0
em.persist(item);
item = em.merge(item);
em.setFlushMode(FlushModeType.COMMIT);//added just in case it would work but it didn't
em.flush();//same as previous line
Item tst = getItemByID(1);
logger.debug("item after:"+item.getItemId()+" ord:"+ord.getOrderId()); //again, orderID is correct, itemID is 0
itemList.add(item);
}
ord.setItems(itemList);
State new_state2 = getStateByName(SharedConstants.STATE_NEW);
logger.debug(ord.getItems().get(0).getItemId()+" order: "+ord.getOrderId());//again, orderID is correct, itemID is 0
}
Order class:
#Entity
#Table(name="orders")
public class Order implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY/*, generator="ORDERS_ORDERID_GENERATOR"*/)
#Column(name="ORDER_ID")
private int orderId;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="CREATED_ON")
private Date createdOn;
//bi-directional many-to-one association to Item
#OneToMany(mappedBy="order")
private List<Item> items;
//uni-directional many-to-one association to User
#ManyToOne
#JoinColumn(name="ASSIGNED_TO")
private User assignedTo;
//uni-directional many-to-one association to User
#ManyToOne
#JoinColumn(name="CUSTOMER")
private User customer;
public Order() {
}
public int getOrderId() {
return this.orderId;
}
}
Item class (removed getters and setters to make it more readable):
#Entity
#Table(name="items")
public class Item implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="ITEM_ID")
private int itemId;
private String code;
private BigDecimal weight;
public BigDecimal getWeight() {
return weight;
}
public void setWeight(BigDecimal weight) {
this.weight = weight;
}
private String comments;//any additional info user'd like to add
private String description;
#Column(name="EXT_ID")
private int extId;
private String manufacturer;
#Column(name="PRICE_EXT")
private BigDecimal priceExt;
#Column(name="PRICE_INT")
private BigDecimal priceInt;
private String region;
private String term;
//bi-directional many-to-one association to Order
#ManyToOne(cascade=CascadeType.PERSIST)
#JoinColumn(name="ORDER_ID")
private Order order;
//bi-directional many-to-one association to State
#ManyToOne
#JoinColumn(name="STATE")
private State state;
}
I had some thoughts about caching so I added to my persistence.xml lines
property name="toplink.cache.type.default" value="NONE"
property name="toplink.cache.type.Order" value="NONE"
but it didn't help either
Try to change int to Integer
private Integer orderId;
and getters and setters as well.
You mention Item is assigned an ID value by the database, but missed the #GeneratedValue(strategy=GenerationType.IDENTITY) annotation you have on order. This is what tells JPA the db controls the value, otherwise it expects the application to set it, keeping it the default 0.
after calling em.persist(obj), call em.flush();. It should work.
better have a private method save
like
private void save(object obj)
{
em.persist(obj);
em.flush();
}